SwarmOps
ReferenceCommandsswarmops diff
View as Markdown

swarmops diff#

List the files that differ between two revisions, a page at a time.

Metadata
Group
read
Since
0.1.0
Mutates
no
Identity required
no
MCP tool
diff
Envelope op
diff
Exit codes
026

Syntax#

swarmops diff [OPTIONS] <FROM> <TO>

Required arguments#

Syntax: <FROM>.

The revision to diff from.

Syntax: <TO>.

The revision to diff to.

Optional arguments#

The command also takes the global options.

Syntax: --path <P>....

Only the files at or below this path; give it again for more.

Syntax: --format <FORMAT>. Default: names.

What to say of each file. Values: names: Its path and status; stat: Also whether it is binary and, for text, the lines it inserts and deletes; unified: Also its kinds, its blobs and, for text, its hunks.

Syntax: --context <N>. Default: 3.

The unchanged lines around each change in a unified hunk.

Syntax: --limit <N>. Default: 500.

The most files to list.

Syntax: --cursor <CURSOR>.

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

Syntax: --max-bytes <N>. Default: 32768.

The most bytes of a unified page's files, as JSON (the first file comes all the same, its hunks cut to fit).

Examples#

swarmops --repo [tmp]/store diff [c1] [c2]
{
  "ok": true,
  "op": "diff",
  "api": 1,
  "result": {
    "from": "[c1]",
    "to": "[c2]",
    "files": [
      {
        "path": "README.md",
        "status": "modified"
      },
      {
        "path": "bin/run",
        "status": "modified"
      },
      {
        "path": "latest",
        "status": "type_changed"
      },
      {
        "path": "logo.bin",
        "status": "modified"
      },
      {
        "path": "notes.txt",
        "status": "deleted"
      },
      {
        "path": "src/lib.rs",
        "status": "added"
      }
    ],
    "truncated": false
  },
  "meta": {
    "repo": "[tmp]/store",
    "elapsed_ms": "[elapsed_ms]",
    "resolved": {
      "[c1]": "[c1]",
      "[c2]": "[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.

No recorded example yet.

Details#

diff compares the trees of any two changesets and lists the files that differ, in path order, a page at a time. --format decides how much it says of each file, from the path and its status up to unified hunks, so an agent can ask for exactly what it needs.

What differs#

FROM is the old side, TO the new; both resolve in one snapshot, and the result names the full ids they resolved to. The two need not be related: any two changesets compare. Swapping them swaps added and deleted. The same changeset on both sides gives files: [].

Only files, executables and symlinks are listed, never a directory. Each has a status:

statusMeaning
addedIn TO only: in FROM nothing, or a directory, is at its path.
deletedIn FROM only.
modifiedIts content or its executable bit changed: a file or an executable on both sides, or a symlink on both sides.
type_changedA symlink on one side, a file or an executable on the other.

A file that becomes a directory is that file deleted and the directory's files added. No rename is detected: a moved file is one deleted and one added.

Formats#

--formatAdds to path and statusReads content
names (default)Nothing.No: trees only.
statbinary; for text, insertions and deletions in lines.Both sides of each listed file, whole.
unifiedAlso old_kind, new_kind, old_blob, new_blob and, for text, hunks.The same.

A side is binary when it is not UTF-8, or holds a NUL in its first 8,000 bytes; a binary file gets no line counts and no hunks. A change of the executable bit alone, or of the kind with the same bytes, is modified or type_changed with no hunks (hunks: []) and 0 lines either way. A side of more than 49,152 lines, or of more than 8 MiB, is not line-diffed: no line counts, and hunks_truncated: true.

Each hunk carries the numbers of a @@ -old_start,old_lines +new_start,new_lines @@ header and its lines in order, each context, remove or add, the removed lines of a change before the added ones; a line keeps its \n, and a \r is part of its line. --context (3 by default) sets the unchanged lines around each change; --context 0 gives the changes alone.

Paths#

--path P keeps the files at or below P: a file's own path takes that file, and a directory's everything below it. Paths match whole components (--path Zeta does not take Zeta.txt), one trailing / is taken, and --path again adds more. An empty path is E_INVALID_ARG. Only the directories a path reaches are read, and a subtree both sides share is never read at all.

Pages#

--limit (500 by default, at least 1) caps the files of a page. A unified page is also held within --max-bytes (32 KiB by default, at least 1) of its files as JSON: it stops before the first file that would take it past that, but always holds one file, whose hunks are cut to the first ones that fit, with hunks_truncated: true. Its insertions and deletions still count every line. names and stat pages ignore --max-bytes.

A cut page says truncated: true and gives cursor, the path of its last file; --cursor starts the next page after that path. A file whose hunks were cut is not sent again on the next page: to see all of it, diff it alone (--path <file>) with a larger --max-bytes, or read both blobs with cat. A cursor that is not a repository path is E_INVALID_ARG; one that names no listed file starts after where it would sort.

Patterns#

  • What one changeset changed. diff X~1 X lists the changes of X against its first parent, a page at a time, which show caps without a cursor. The first changeset of a history has no parent to diff against: show lists its changes.
  • What a change does since its base. diff <base> <head> of an agent's work shows what a land would publish, before the merge with what the target gained.
  • Cheap first, then detail. A names page reads no content; ask for unified with --path for only the files you need to read.
  • Pin both sides. Give full ids, the from and to of the first page, to every later page: the cursor is a path and does not pin the revisions, so pages of main can come from two revisions if another agent lands in between.
  • Through MCP, the tool is diff, with from, to, paths (an array), format, context, limit, cursor and max_bytes. Ask for 100 files or fewer a call, and max_bytes of 32,768 or less for unified: see Result size limits.

Caveats#

  • stat and unified read both versions of every listed file whole, and build its hunks before the page is cut to --max-bytes, so memory and time follow the files, not the page. names reads no content. See known limitations.
  • --limit 0 and --max-bytes 0 are E_INVALID_ARG.

Guides#

Return information#

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

FieldTypeRequiredDescription
cursorstring or nullnoWhen truncated: where the next page starts. Opaque: pass it back as given, as the next request's cursor.
filesarray of FileDiffyesThe files.
fromstringyesThe changeset from named.
tostringyesThe changeset to named.
truncatedbooleanyesWhether more files follow this page.

Envelope op: diff.

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 (exit 6): A revision names no changeset.
  • 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, E_IO or E_STORE_CORRUPT; a command line the grammar refuses is E_INVALID_ARG (exit 2).

See also#

Edit on GitHub

On this page