# aura diff *Show a semantic diff: logic nodes added, removed, modified, renamed, or moved.* ## Synopsis ```bash aura diff [] [] [--semantic] [--textual] [--stat] [--name-only] [--paths ...] [--format=text|json] ``` ## Description `aura diff` is the semantic counterpart to `git diff`. It compares two states of the codebase and reports the differences in terms of **logic nodes** — functions, methods, classes, types, constants — rather than lines of text. A refactor that moves a function to a new file shows up as a single *moved* entry instead of a big block of deleted lines in one file and added lines in another. A rename shows as one *renamed* entry with the old and new names, not as the disappearance and reappearance of two unrelated functions. Whitespace, import reordering, and comment-only changes are ignored. With no arguments, `aura diff` compares the working tree against the last save. With one argument, it compares the working tree against that ref. With two, it compares them to each other. A ref can be a Git commit, a branch name, an Aura save id, a snapshot id, or the keywords `HEAD`, `SAVE`, or `MOTHERSHIP` (the last-known teammate state). ## Flags | Flag | Description | | --- | --- | | `--semantic` | Default. AST-level diff. | | `--textual` | Fall back to a text diff with AST-aware hunk boundaries. | | `--stat` | Summary counts per file and per node kind. | | `--name-only` | Print only the qualified names of changed nodes. | | `--paths ...` | Restrict the diff to the given paths. | | `--format=text` | Default, colored. | | `--format=json` | One change per line, machine-readable. | | `--include-bodies` | Inline full before/after bodies for modified nodes. Default shows unified hunks. | | `--include-whitespace` | Do not ignore whitespace-only changes. | ## Output format Each change entry carries an operation, a node identity, and a location. Operations are: | Op | Meaning | | --- | --- | | `added` | Node exists in B but not in A. | | `removed` | Node exists in A but not in B. | | `modified` | Node exists in both with different bodies. | | `renamed` | Same identity, different name. | | `moved` | Same identity, different file. | | `signature-changed` | Name preserved, but parameter list or return type changed. | | `visibility-changed` | `pub`/`private`/`export` changed. | | `body-only` | Body changed but signature did not — a subset of `modified`, shown separately when useful. | A typical text output: ```text aura diff HEAD..SAVE src/payments/gateway.rs modified fn PaymentGateway::charge +14 -2 lines; intent-linked: "Add retry_on_network_error" signature-changed fn PaymentGateway::refund (amount: Money) -> (amount: Money, reason: RefundReason) added fn PaymentGateway::capture src/payments/errors.rs -> src/payments/error.rs (file renamed) moved enum PaymentError added variant PaymentError::NetworkTimeout src/legacy/cvv.rs removed fn check_cvv last seen in save sv_0198ff; restored available via `aura rewind check_cvv` Summary: +3 nodes, -1 node, ~3 modified, 1 renamed-or-moved across 3 files ``` ## Comparison to git diff - **Scope.** `git diff` shows text hunks grouped by file. `aura diff` shows operations grouped by logic node. - **Rename detection.** Git's `-M` is heuristic on file content similarity and only applies to files. Aura's rename tracking is identity-based and applies to functions. - **Noise.** A reformat, a rename-only change, or an import reorder produces zero entries in `aura diff`. The same change in `git diff` can be thousands of lines. - **Merge previews.** `aura diff branch-a branch-b` shows exactly which logic nodes conflict if you merge, which git diff cannot answer directly. - **Intent linkage.** Each modified node links to the save whose intent produced it; git diff has no equivalent. `aura diff --textual` falls back to a text-line diff when you need to see exact formatting changes. The textual mode still uses AST-aware hunk boundaries, so a hunk never spans two unrelated functions. ## Examples ### 1. What did I change since my last save? ```bash aura diff ``` The default — working tree vs last save. The most common invocation. ### 2. What will my next save record? ```bash aura diff --stat ``` ```text aura diff HEAD..working src/api/auth.rs 2 modified, 1 added src/session/store.rs 1 modified src/models/user.rs 1 renamed (User -> Account) Totals: +1 added ~3 modified 1 renamed 0 removed ``` Compact preview before running `aura save`. ### 3. Compare two branches ```bash aura diff main feat/account-rename --name-only ``` ```text renamed fn User::new -> fn Account::new renamed fn User::validate -> fn Account::validate modified fn SessionStore::get_user modified fn api::users::handler added fn api::accounts::handler ``` Exactly the node-level changes. No file-level noise. ### 4. Compare against teammates' work ```bash aura diff MOTHERSHIP ``` Shows what has arrived from teammates via the Mothership but not yet been pulled into your working tree. Pair with `aura pull`. ### 5. Machine-readable diff for a bot ```bash aura diff HEAD~5..HEAD --format=json ``` ```text {"op":"modified","node":"fn PaymentGateway::charge","file":"src/payments/gateway.rs","save":"sv_019abc","intent":"Add retry_on_network_error"} {"op":"added","node":"fn PaymentGateway::capture","file":"src/payments/gateway.rs","save":"sv_019abc"} {"op":"removed","node":"fn check_cvv","file":"src/legacy/cvv.rs","last_save":"sv_0198ff"} {"op":"renamed","from":"fn User::new","to":"fn Account::new","file":"src/models/user.rs","save":"sv_019a10"} ``` One change per line. Feed it into a code-review bot or a changelog generator. ## Exit Codes | Code | Meaning | | --- | --- | | `0` | Diff produced (may be empty). | | `1` | Generic failure. | | `2` | A ref could not be resolved. | | `3` | Both refs refer to the same state (nothing to diff) and `--strict` was set. | ## Limits and gotchas A semantic diff is only as good as the AST parser for the language. Languages with mature tree-sitter grammars (Rust, Python, TypeScript, Go, Java, C++, Ruby) produce excellent diffs. Languages without — or configuration files, templating languages embedded in strings, code generation output — fall back to textual diff with a warning. `aura doctor --check=IDX003` shows which files are affected. Macro-heavy code can appear to change semantically when only a macro's expansion differs; Aura parses the pre-expansion source, so macro internals that change are invisible to the diff. Usually this is what you want; occasionally it hides a real behavioral change. When in doubt, cross-check with `--textual`. ## Reading a semantic diff in code review In practice, `aura diff` changes what a code reviewer looks at. Instead of scrolling through hundreds of lines of re-indented imports to find the six lines that matter, the reviewer sees six logic-node entries. For each, they can expand the body with `--include-bodies` or drop into `aura trace` to understand the history of that function. Reviewers also get the intent line attached to every modified node. A save is a named unit of reasoning; the node-level diff re-exposes that reasoning inline. This is particularly valuable for PRs produced by AI agents, where the difference between *I changed this because X* and *I changed this and the commit message does not explain why* is the difference between an approvable PR and one that requires another round. ## Diffing with the Mothership view `aura diff MOTHERSHIP` compares your working tree to the union of all pending teammate deltas. This is a preview of *what the codebase is about to look like* once you pull. Running it before `aura pull` tells you whether a teammate's pending change will affect anything you are currently touching. Similarly, `aura diff HEAD MOTHERSHIP` shows only the teammate-originated changes — useful in standup or asynchronous review: "here is what the team did since my last commit." ## Diffing across rewrites When `git rebase` has rewritten commits, line-level diffs across the rewrite are unreliable. `aura diff` is stable across rewrites because identities are preserved: a rebase does not mint new logic-node identities, it only re-parents their saves. `aura diff old-sha new-sha` where `new-sha` is the rebased counterpart of `old-sha` shows the *actual* semantic change introduced by the rebase (often nothing, which is reassuring). ## How the diff is computed Computing an AST diff naively is quadratic in the number of nodes, which is too slow on large repositories. Aura uses a two-phase strategy. The first phase **matches by identity**: for each node in A, look up the same identity in B's index. Matches pair off and their bodies are hash-compared. Unmatched nodes in A are candidates for deletion or rename; unmatched in B are candidates for addition or rename. The second phase resolves **rename candidates** via a Hungarian-style assignment: for each unmatched A-node and each unmatched B-node, score the similarity of their bodies and signatures. Pairs above a threshold (default 0.75) are reported as `renamed` or `moved`; the rest as `removed`/`added`. Ties are broken by proximity (same file, same module). This is fast in practice because the identity lookup handles the 99% case — most nodes do not change identity between saves — and the expensive second phase only runs on the small residual set. ## Ignored changes By design, the following are not reported: - Whitespace and indentation. - Import ordering and grouping. - Trailing commas where optional. - Doc-comment edits, unless `--include-docs` is set. - Changes in file encoding or line endings. These categories can be individually re-enabled in `aura.toml` under `[diff.include]` when you need to audit them (for example in a code-style sweep). ## See Also - [aura trace](/aura-trace) — history of a single node - [aura save](/aura-save) — what `aura diff` with no args is previewing - [aura merge](/aura-merge) — `aura diff branch` is a preview of what the merge will consider - [aura status](/aura-status) — summary of changes without the full diff