---
description: "Every git-span subcommand, grouped by task, with flags and exit behavior."
---

# Command reference (/docs/commands)

{/* GENERATED by gen-schemas. Do not edit by hand. */}

## Global options

Every subcommand accepts:

* `--perf` — emit performance timings for major git-span operation groups to stderr. Can also be enabled with `GIT_SPAN_PERF=1`.
* `-h`, `--help` — print help (a summary with `-h`, full help with `--help`).
* `-V`, `--version` — print the version (top-level only).

Bare `git span` with no arguments prints help and exits 0 — it does not list every span. Run `git span list` with no arguments for that. Any clap usage error (bad flag, missing required argument) exits 2.

## Declare and edit

### add

```bash
git span add [OPTIONS] <NAME> <ANCHORS>...
```

Add anchors to a span, writing the span file under the span root.

Each anchor is either a bare `<path>` (whole-file) or `<path>#L<start>-L<end>` (line range, 1-indexed, inclusive); for example, `git span add api-contract src/api.ts#L1-L3 tests/api.test.ts`. With `--at <commit-ish>`, every anchor in this invocation is hashed against the file content at that commit instead of the working tree — hashing against a commit-ish rather than the working tree can make a new anchor itself drifted when the working-tree content differs; the span-wide line reports it honestly.

Re-adding an address the span already tracks does not duplicate the anchor — it is resolved in place, re-hashed against the current content (`Added 0 anchors and resolved 1 in place`). That is the idiom for confirming an intentional content change `drift` flagged on a range that did not move — after checking that the span's other anchors still agree with the new content; a coupled doc that now lags is updated first.

Every `add` ends with a scoped post-write check over the touched span. After the requested-address lines it prints superseded old anchors and old anchors that remain drifted — each with a runnable `git span remove` next action — then one span-wide line that alone asserts span-wide state: `0 drift across ...`, `N anchors drifted — ...`, `state indeterminate (index changed during check)`, or `state unverified`. The requested-address lines never assert span health; "clean" appears only as the check's span-wide fact. `add` shares `drift`'s exit contract: 0 = clean, 1 = drift remains or the check errored, 2 = indeterminate (index changed during check) — retryable. Exits 1 on an invalid name or an out-of-range anchor.

Stage and commit the change with `git add .span && git commit`.

Key flags:

| Flag                     | Description                                                                                                                                                                         |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--at <COMMIT-ISH>`      | Hash every anchor in this invocation against the file content at `<commit-ish>` (an ordinary git commit-ish). When omitted, anchors are hashed against the working tree             |
| `--format <human\|json>` | Output format for the write-mode result (`human` or `json`). <br /> Applies to the write mode: `json` emits the mutation document (schema\_version 1) instead of the human summary. |

### remove

```bash
git span remove <NAME> <ANCHORS>...
```

Remove anchors from a span, editing the span file under the span root. Each anchor must match an existing one on the span exactly. Stage and commit the change with `git add .span && git commit`

### replace

```bash
git span replace [OPTIONS] <NAME> <OLD_ANCHOR> <NEW_ANCHOR>
```

Replace one anchor on a span with another address, in a single atomic transaction: either the old identity is retired and the new identity installed together, or nothing changes — there is never an intermediate state with both or neither. The new anchor passes the same validation as `add` (path safety, existence, gitignored/rewritten-target rejection, line-count extent), so a poisoned declaration is never written.

The old address must match an existing anchor exactly — `replace` never falls back to additive behavior. To refresh an anchor's content hash at an unchanged address, use `git span add` instead. Refusals (all leave the declaration byte-for-byte unchanged, exit 1): - `old-anchor` matches no anchor on the span, or matches more than one record (a hand-edited or legacy declaration can hold two same-identity records with different hashes — no canonical choice exists). - `old-anchor` and `new-anchor` are the same identity — in-place hash refresh is `add`'s job; `replace` changes identity. - `new-anchor` is already tracked by the span — the swap would leave two same-identity records behind; retire the old anchor with `remove` instead.

On success the output names the retired and installed addresses and reports whether the span is drift-free; `--format json` carries `span`, `retired`, `installed`, `drift_free`, and the `drifted` addresses.

Stage and commit the change with `git add .span && git commit`.

Key flags:

| Flag                     | Description                   |
| ------------------------ | ----------------------------- |
| `--format <human\|json>` | Output format (human or json) |

### why

```bash
git span why [OPTIONS] <NAME> [WHY_TEXT]
```

Read or stage the span's why — one or two complete present-tense clauses carrying decision-relevant nonlocal context.

State the relationship plus any decisive authority, invariant, permitted difference, lifecycle state, evidence gate, or focused conditional verification. Use role words rather than file names and give every clause a subject and verb. Labels such as `Authority:` or `Removal gate:` are optional, but must introduce complete clauses. Omit generic work orders and CLI procedure.

Bare `git span why <name>` prints the current why; a positional argument writes a new why into the span file, and bare non-terminal invocations fail instead of reading stdin. A write ends with the same post-write span-wide check as `add` and shares its exit contract (0 clean / 1 drift remains or check errored / 2 index changed, retryable) — the output prints `Set why on span <name>. (idempotent)`, then any remains lines and the span-wide line; superseded lines are absent because `why` touches no addresses. `--format json` applies to the write mode only: it emits the mutation document (`command: "why"`, `why_written: true`) instead of prose, and read mode rejects it fail-closed (exit 1, no stdout) rather than printing prose.

The why is inherited across routine re-anchors only while its meaning remains true. Revise or retire it when the relationship or lifecycle state changes. Commit with `git add .span && git commit`.

Key flags:

| Flag                     | Description                                                                                                                   |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| `--format <human\|json>` | Output format for the write-mode result (`human` or `json`). <br /> Applies to the write mode; read mode always prints prose. |

### config

```bash
git span config <NAME> [KEY] [VALUE]
```

Read or set a span's resolver configuration — the span file's trailing `[config]` block.

Bare `git span config <name>` prints the span's effective configuration: all three keys with their current values, defaults included, so the output answers what the span is actually doing rather than what the file happens to say.

`git span config <name> <key> <value>` sets one key and reports the transition it made. The accepted vocabulary is the parser's vocabulary: keys are `copy_detection`, `ignore_whitespace`, and `follow_moves`; values are the documented wire names (`off`, `same-commit`, `any-file-in-commit`, `any-file-in-repo`) for `copy_detection` and `true`/`false` for the two flags. An unknown key or an out-of-vocabulary value is rejected before any file I/O, and the span file is left byte-identical.

Writes go through the same serializer as every other mutating command, under the exclusive repository lock: a span whose configuration returns to the documented defaults (`same-commit` / `false` / `false`) loses its `[config]` block entirely, and a no-op write says so while rewriting nothing. A span whose file does not parse — including a Git-conflict-markered one — is still refused; recovery for such spans is `git span resolve`'s job.

Stage and commit the change with `git add .span && git commit`.

### delete

```bash
git span delete <NAME>
```

Delete a span — removes its file under the span root. Destructive. Run `git span list` afterward to confirm it is gone, then commit the change

## Inspect

### show

```bash
git span show <NAME>
```

Show the named span — its anchors, why, and config. The bare `git span <name>` positional form is equivalent to `git span show <name>`.

A span whose file is in a Git conflict state is refused rather than rendered from conflict-marker text; `git span resolve` settles it.

### list

```bash
git span list [OPTIONS] [TARGETS]...
```

List files and anchors currently tracked by a span

Key flags:

| Flag           | Description                                                   |
| -------------- | ------------------------------------------------------------- |
| `--porcelain`  | Emit one tab-separated row per anchor instead of human blocks |
| `--offset <N>` | Skip the first N spans (after filtering, before --limit)      |
| `--limit <N>`  | Cap output at N spans (after filtering and --offset)          |
| `--oneline`    | One line per anchor: `<span-name>` `<canonical-address>`      |

### tree

```bash
git span tree [OPTIONS] <GLOBS>...
```

Trace blast radius: render a clique-grouped impact tree rooted at the files matched by the given paths/globs.

Starting from the matched anchor paths, the tree expands outward through span co-occurrence to the files each could affect. Files that all anchor the same span — and are therefore mutually connected — collapse onto a single comma-separated line and expand once as a unit, so a cluster that moves together reads as one line.

Roots are file paths and globs only, resolved repo-relative with the same `globset` matching and exact-path lookup as `list`/`drift` (no CWD-relative joining). Unlike `list`/`drift`, `tree` does NOT accept `#L<start>-L<end>` line-range addresses or bare span names. At least one argument is required, and a pattern matching no anchored file is an error. `-d`/`--depth` bounds the expansion (default 3; `--depth 0` prints roots only). `--format human` (the default) prints the nested markdown list; `--format json` emits the same structure as nested `{ "members": [...], "children": [...] }` nodes for tooling.

Multiple roots are supported and encouraged: pass every file whose blast radius you're tracing in one call — e.g. `git span tree fileA fileB fileC --depth 2` — rather than invoking `tree` once per file and merging the outputs by hand. All roots are unioned before clique grouping, so files that share a span collapse onto one clique line exactly as they would from a single-root call, while roots with no span in common still surface as separate top-level trees in the same listing.

Key flags:

| Flag                     | Description                              |
| ------------------------ | ---------------------------------------- |
| `-d, --depth <N>`        | Maximum expansion depth (0 = roots only) |
| `--format <human\|json>` | Output format                            |

### history

```bash
git span history [OPTIONS] <SPAN>
```

Show a span's git history as a git-log-style timeline: newest-first commit entries whose patches are real unified diffs of the span declaration and of each anchor's content at its declared address, rename-aware by content similarity — a re-anchor whose old and new content pair at ≥ 50% similarity (git's `-M` default) renders as a rename, a sub-threshold re-anchor renders as a deleted anchor plus a new anchor, and a re-anchor whose recorded side cannot be read (binary or unrecoverable content) renders `rename from`/`rename to` lines with no similarity claim, the move asserted by the declaration itself. The span's live drift is rendered before the first commit as a headerless diff. That drift is not working-tree-only: it covers every resolver layer `git span drift` reports, and each block names its observational layers on a `drift source` line. `HEAD` identifies the comparison layer; it does not by itself prove that the declaration or content change was committed. Source order is the resolver's: line ranges use Worktree → Index → Head, while whole-file anchors use Index → Worktree → Head.

Outputs human-readable text by default; use `--format json` for `schema_version: 2` JSON carrying the same patches as raw text. When `--limit` drops older qualifying commits, the requested window is still printed but a warning goes to stderr, and JSON output sets `scoped: true` (omitted, not `false`, when the timeline is the complete record). Treat scoped JSON output as a partial record — never read it as evidence that a span has no history or no drift.

Key flags:

| Flag                     | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--format <human\|json>` | Output format (human or json)                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `-n, --limit <LIMIT>`    | Show only the newest N entries. <br /> N counts *rendered* timeline entries, not walked commits: the walk is always complete, so a narrow anchor in a busy file can never fill the window with commits that changed nothing. Older entries are dropped, a warning goes to stderr, and JSON output carries `scoped: true` — such a document is a partial record, so never read it as evidence that a span has no history or no drift. `-n 0` yields an empty, scoped document. |

### context

```bash
git span context [OPTIONS] [ADDRESSES]...
```

Return exact dependency context for repository-relative paths or inclusive line ranges as one versioned JSON document.

With `--fix`, position-only or whitespace-equivalent drift is repaired before reporting, and `--operation-id` keys a repair retry so the same attempt is idempotent. Output is always the versioned context document.

Key flags:

| Flag                    | Description                                                               |
| ----------------------- | ------------------------------------------------------------------------- |
| `--format <json>`       | Output format (`json` — the versioned context document is the only shape) |
| `--fix`                 | Repair position-only or whitespace-equivalent drift before reporting      |
| `--operation-id <UUID>` | Stable idempotency key for a repair retry. Valid only with `--fix`        |

## Audit and automate

### drift

```bash
git span drift [OPTIONS] [PATHS]...
```

Report anchors whose content has drifted from their anchored state. `paths` may be file paths, globs, or span names; omit them to scan all spans.

With `--fix`, also re-anchors `Moved` anchors unconditionally and whitespace-equivalent `Changed` anchors in place, and resolves `.span/` merge conflicts structurally. A `Changed` anchor whose content differs beyond whitespace is left drifting so the coupling resurfaces for human confirmation — the operator reviews the rewritten spans with `git diff` and stages only what they agree with. Conflict resolution splits Git textual conflict markers into ours/theirs, enforces a clean-source precondition (all referenced source files must be conflict-free), and calls the structural merge kernel.

Fail-closed cases: - A referenced source file itself contains conflict markers. - The `--why` text diverged between ours and theirs with no merge base (textual marker split — no base available).

Fully resolved conflicts are written clean and re-anchored in the worktree; spans with residual divergence (same anchor, different range/hash with no clean source) or a divergent `--why` write the resolved anchors cleanly with minimal residue markers and are not re-staged.

`--format human` (the default) prints a summary line on a clean scan (`0 drift across M spans (K anchors checked)`) and per-span listings (each anchor, then the span's why) when drift is found. `--format json` always emits a document: a clean scan carries `"clean": true` (schema\_version 3) — never empty stdout; informational `RESOLVED_PENDING_COMMIT` findings may still be listed, so `clean` does not imply `findings: []` — and a drifted scan carries `"clean": false` plus the findings. `--format porcelain` prints nothing on a clean scan (no header row).

> [!WARNING]
> `--fix` re-anchors `Moved` anchors unconditionally — pure line shifts and file renames alike — and whitespace-equivalent `Changed` anchors (indentation-only edits), tagging them `resolved, pending commit — auto-updated` on the run that fixes them. Meaning-altering `Changed` drift — content differs beyond whitespace — is always left for you to resolve with `git span add`; re-running `--fix` will not touch it. After `--fix`, re-run `git span drift` and re-anchor anything still drifting yourself. `--fix` never commits; it only rewrites `.span/` worktree files — stage and commit with `git add .span && git commit`.

Exit codes: 0 (no drift, or `--no-exit-code` with drift present), 1 (drift found, or an interior-anchor integrity violation — the latter is never maskable by `--no-exit-code`).

A span `--fix` cannot finish is not analyzable by re-running `drift`. See `git span resolve` (`--dry-run` first), which settles a conflict-markered span file under one explicitly chosen side.

Key flags:

| Flag                                | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--format <human\|porcelain\|json>` | Output format (`human`, `porcelain`, or `json`; default `human`). See the command description for what each format prints on clean and drifted scans                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `--no-exit-code`                    | Exit 0 even when drift is found (report-only mode). Does not mask the interior-anchor integrity case                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `--perf-trace <PATH>`               | Write a CSV of per-anchor wall-clock traces to PATH. Requires a full scan (no positional paths). Columns: span,anchor\_id,anchor\_sha,path,wall\_us,fast\_path,status. See the wiki page "Profiling git span drift" (wiki/guides/) for schema and examples                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `--fix`                             | Re-anchor `Moved` anchors unconditionally and whitespace-equivalent `Changed` anchors in place by rewriting the span worktree files. A `Changed` anchor whose content differs beyond whitespace is left drifting — the stored hash gates on content-equivalence so that meaning-altering edits keep surfacing until the operator confirms them. Each surfacing anchor is re-hashed against the deepest drifting layer (`Worktree > Index > HEAD`). Also resolves `.span/` merge conflicts structurally: splits conflict markers into ours/theirs, enforces a clean-source precondition (all referenced source files must be conflict-free), and calls the structural merge kernel. Fully resolved spans are written clean; spans with residual unresolvable anchors or divergent `--why` text (no merge base) write resolved anchors cleanly with minimal residue markers and are not re-staged. No commit is produced. Only supported with `--format human` |
| `--cluster`                         | Group this run's drifted spans into connected-component clusters by shared anchored file, so each cluster can be dispatched independently. Rendered as an additional section/field in every `--format`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |

### doctor

```bash
git span doctor
```

Audit the local span setup — reports span count and findings, plus a store-size summary (bytes used). Exits 0

### merge-driver

```bash
git span merge-driver <BASE> <OURS> <THEIRS> <MARKER_LEN>
```

Resolve `.span/` merge conflicts structurally when invoked by git as a merge driver. Receives three clean blob temp files and the marker length from git. Resolves only what is structurally derivable without trusting the worktree, which may be mid-merge. Same-anchor range/hash divergence is deferred: minimal conflict markers are written and the command exits non-zero so `git span drift --fix` can finish authoritatively. Exits 0 when fully resolved, 1 when partial (residue markers written).

Register in `.gitattributes`:

```text
.span/** merge=span
```

Register in `.git/config`:

```ini
[merge "span"]
name = git-span structural span merge
driver = git span merge-driver %O %A %B %L
```

### resolve

```bash
git span resolve [OPTIONS] <NAME>
```

Settle every residue entry in a conflict-markered span file under one explicitly chosen side, and write a clean span.

This is the one command whose supported input is a span file carrying Git conflict markers — the file `git span merge-driver` leaves behind when it cannot resolve an anchor without trusting the worktree. On a file without markers it is a no-op that says so.

`--rehash` (the default) re-reads each conflicted anchor's source from the worktree and hashes it. `--ours`/`--theirs` choose between conflicting values for the same anchor key; anchors declared by only one side remain in the union. They also choose that side's prose for a divergent `--why`, which is the path for a source that cannot be re-read at all.

Resolution is all-or-nothing per span: any entry that cannot be settled under the chosen side leaves the file byte-identical and names what stopped it, so a failed run is always safe to retry with another side. The result is written to the worktree and never staged — review it with `git diff` and stage what you agree with.

Key flags:

| Flag                     | Description                                                                                                                                                                                                                   |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--rehash`               | Re-read each conflicted anchor's source file from the worktree and compute its hash. Default when no side flag is given                                                                                                       |
| `--ours`                 | For a conflicting value on the same anchor key, keep this side's record; anchors declared only by theirs remain in the union. Keep this side's prose/settings for a divergent `--why` or `[config]`                           |
| `--theirs`               | For a conflicting value on the same anchor key, keep the other side's record; anchors declared only by ours remain in the union. Keep its prose/settings for a divergent `--why` or `[config]`                                |
| `--dry-run`              | Report what EACH of `--rehash`/`--ours`/`--theirs` would produce, without writing. Side flags are ignored when this is set — all three are always evaluated together so the outcomes can be compared before committing to one |
| `--format <human\|json>` | Output format (human or json)                                                                                                                                                                                                 |
