Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

JSON output (--json)

margin diff --json, margin show --json, and margin patch --json emit the parsed changeset as a single JSON document on stdout — how agents and scripts consume Margin without scraping a terminal.

margin diff --json | jq '.files[] | {path: .new_path, adds: .additions}'
git show HEAD --format= | margin patch --json - | jq '.additions'

pager mode never emits JSON: its piped output is byte-identical to its input by contract (ADR-0007). --json cannot combine with --watch.

Exit status

0 means a clean document; 1 means the document is usable but the parser warned (warnings go to stderr, never into the JSON). 2 means the invocation itself was invalid (ADR-0022).

Stability

The document carries "schema": 1. Within schema 1, changes are additive only (new optional fields may appear; nothing is removed or retyped). A breaking change would ship as "schema": 2 — consumers should check the field.

Encoding

Margin’s model is bytes-first; JSON strings are UTF-8. Every string in the document is lossy UTF-8: invalid bytes become U+FFFD (�), and any value that was altered carries a sibling flag — "lossy": true on lines, "path_lossy": true on files, "heading_lossy": true on hunks. The flags are omitted when false. Consumers needing exact bytes should read the raw diff itself; the JSON view is for structure.

Schema 1

{
  "schema": 1,
  "files": [
    {
      "status": "modified",        // added | deleted | modified | renamed | copied
      "old_path": "src/app.rs",    // null for added files
      "new_path": "src/app.rs",    // null for deleted files
      "path_lossy": true,          // OMITTED unless a path had invalid UTF-8
      "binary": false,             // binary files carry no hunks
      "old_mode": "100644",        // octal string, git-style; null when unknown
      "new_mode": "100755",
      "similarity": 90,            // OMITTED except for renames/copies with an index
      "additions": 3,              // line counts for this file
      "deletions": 1,
      "hunks": [
        {
          "old_start": 1, "old_count": 5,
          "new_start": 1, "new_count": 6,
          "heading": "fn main()",  // the @@ ... @@ section text; OMITTED when absent
          "heading_lossy": true,   // OMITTED unless the heading had invalid UTF-8
          "lines": [
            {
              "kind": "context",   // context | addition | deletion
              "content": "…",      // no +/-/space marker, no trailing newline
              "lossy": true,       // OMITTED unless content had invalid UTF-8
              "no_newline": true   // OMITTED unless "\ No newline at end of file"
            }
          ]
        }
      ]
    }
  ],
  "additions": 3,                  // totals across all files
  "deletions": 1
}

The producing types live in margin-core/src/json.rs — a deliberate public surface, decoupled from the internal model so refactors cannot change this document shape silently.