Semantic diff vs git diff: what changes when you compare structure
A git diff compares lines. A semantic diff compares the parsed structure of the code, so a rename is a rename and a move is a move. What that changes in practice, and what it costs.
A Git diff compares lines of text. A semantic diff parses both versions into syntax trees and compares those, so it can say a function was renamed, moved or had its body changed, rather than reporting that forty lines vanished and forty appeared. The difference is small on a two-line fix and large on anything an agent writes.
The same change, two ways
Move a function from the bottom of a file to the top and rename it. Git reports the whole block as deleted and the whole block as added: two hunks, sixty lines, no relationship between them. A structural comparison reports one node, moved, renamed, body unchanged. The second description is both shorter and more accurate, and it is the one a reviewer needs.
| Edit | Git diff | Semantic diff |
|---|---|---|
| Rename a function | Every line of it, twice | One rename, plus the call sites |
| Reformat a file | The whole file | Nothing changed |
| Wrap a block in a condition | Every line re-indented | One node gained a parent |
| Delete a function | A deletion among many | A named symbol removed, with its callers listed |
| Change one boolean | One line | One line, and everything that depends on it |
The last row is the one that matters most and gets the least attention. Git can tell you what changed. It cannot tell you what the change reaches.
How it works
Both versions of the file are parsed into an abstract syntax tree, usually with tree-sitter, which is fast enough to run on every save and tolerant of code that does not compile. Each node is then reduced to a stable identity: a function is identified by its position in the tree and its contents, not by its line number. Comparing the two trees gives added, removed, changed and moved nodes. Adding a hash per node, computed from the node and its children, makes the comparison cheap: identical subtrees have identical hashes and are skipped whole. That is a Merkle graph.
What it costs
- A parser per language. Support is per-grammar, so an unsupported language falls back to text.
- Parse time on first index. Incremental after that, since only edited files are re-parsed.
- Ambiguity in edge cases: a function deleted and a similar one added can be reported as a change or as both, and different tools pick differently.
Where it earns its keep
Agent-written changes, mostly. A 4,000-line diff from an autonomous session is unreadable line by line, and most of those lines are moves, renames and reformatting. Removing that noise leaves the twenty or thirty places where a decision was made, which is a review a person can actually do. It is also what makes an automatic check against the goal possible: you cannot ask whether a goal was delivered without a structure to ask about.
Related tools worth knowing: difftastic for a structural diff on the command line, and GumTree for the tree-differencing research this all descends from. Aura keeps the same structure as a live index so the diff, the impact analysis and the goal proof all read from one graph.
What is a semantic diff?
A comparison of two versions of a program by their parsed structure rather than their text. Where a line diff reports characters that changed, a semantic diff reports that a function was renamed, a parameter added, a branch removed — the units the language actually has.
What is the difference between a semantic diff and git diff?
git diff compares lines and knows nothing about the language, so reformatting looks like change and a moved function looks like a deletion plus an addition. A semantic diff parses both sides first, so a move is a move, a rename is a rename, and formatting is silent.
Why does git diff show changes when only formatting changed?
Because it compares text. Re-wrapping a line, changing indentation or reordering imports alters the characters without altering the program, and a line-based comparison has no way to tell those apart from a real edit.
Is a semantic diff better than a text diff?
For review, usually — it separates what changed from how it was typed. For everything else, not necessarily: it needs a working parser for the language, it is slower, and it cannot show you edits inside strings, comments or config that a text diff shows plainly.
How does a semantic diff work?
Parse both versions into syntax trees, hash each node from its children upward, then compare the hashes. Identical subtrees match immediately and are skipped, so the comparison only descends into the parts that genuinely differ, and matches survive a node moving.
Aura Crew vs Claude Code Loops: the autonomous work-loop, compared
Claude Code defines four loop types — turn-based, goal, time, and proactive. Aura Crew is the same idea productized: a dependency-ordered work-loop with proof, collision-safety, and any agent. Full comparison.
Autonomous coding agent loops, explained: turn-based, goal, time, and proactive
A coding agent loop is an agent repeating cycles of work until a stop condition is met. The four types explained — turn-based, goal-based, time-based, proactive — with when to use each and how to keep quality high.
How to run multiple coding agents in parallel without merge conflicts
Running several AI coding agents at once collides on shared files. The fix: a worktree per agent, a live team radar, and soft-to-hard zone claims so parallel agents never touch the same symbol. How Aura Crew does it.
Goal-based agent loops: giving a coding agent a definition of done
A goal-based loop keeps a coding agent iterating until a success criterion is met. The trick is a deterministic definition of done — tests, a score, a proof — not an LLM guessing at "good enough." How to write one.