Diff Command¶
The diff command computes a semantic, AST-level diff between two fastC
sources. It reports added / removed pub items and signature changes —
ignoring whitespace, comment edits, and body churn so PR review hooks
only see what actually moves the API surface.
Usage¶
Arguments¶
| Argument | Description |
|---|---|
<OLD> |
Path to the previous source |
<NEW> |
Path to the new source |
Options¶
| Option | Description |
|---|---|
--format <FORMAT> |
Output format: markdown (default) or json |
--include-bodies |
Reserved for a future body-aware diff slice — currently a no-op |
-h, --help |
Print help |
Markdown Output¶
The markdown form has three sections, each suppressed when empty:
# Semantic diff
## Added (+N)
- name
## Removed (-N)
- name
## Changed (N)
- **name**
- was: `signature`
- now: `signature`
If nothing semantically changed, output collapses to:
JSON Output¶
{
"added": ["name", "..."],
"removed": ["name", "..."],
"changed": [
{ "name": "name", "old": "signature", "new": "signature" }
]
}
The signature strings are compact textual renderings of the parameter
list and return type — same form fastc explain uses.
Worked Example¶
Old (v1.fc):
New (v2.fc):
pub fn read(cap: ref(CapFsRead), buf: mref(slice(u8))) -> usize {
return 0;
}
pub fn open(path: ref(slice(u8))) -> i32 {
return 0;
}
Running:
Output:
# Semantic diff
## Added (+1)
- open
## Removed (-1)
- close
## Changed (1)
- **read**
- was: `fn read(buf: mref(slice(u8))) -> usize`
- now: `fn read(cap: ref(CapFsRead), buf: mref(slice(u8))) -> usize`
JSON form:
{
"added": ["open"],
"removed": ["close"],
"changed": [
{
"name": "read",
"old": "fn read(buf: mref(slice(u8))) -> usize",
"new": "fn read(cap: ref(CapFsRead), buf: mref(slice(u8))) -> usize"
}
]
}
What Counts as a Change¶
Today the diff considers a function "changed" when its signature summary
shifts — parameter names, parameter types, or return type. Body edits
are ignored. The diff walks pub fn items only; private functions are
intentionally invisible.
Future revisions will extend the signature summary to cover annotation
changes (@purity, @panics, @requires, @ensures) and module
header drift. The schema is additive — existing fields keep meaning.
When to Use It¶
- PR review hooks: post a comment summarizing the API surface delta before a human reviewer reads the diff.
- CI gates: fail the build if
## Removedis non-empty on a minor-version branch. - Changelog drafting: a starting point for the "what changed" section of a release note.
- Refactor verification: ensure a pure rename / extract didn't accidentally shift a public signature.
Limitations¶
--include-bodiesis reserved but currently does nothing. The body-aware slice will land alongside structured fixit support.- The diff is one-file-at-a-time; project-wide diffing requires shelling
the command per file pair (or use the MCP
difftool from an agent). - Items renamed in place are reported as one Added + one Removed pair, not as a Renamed event. Future versions may add a heuristic match.
See Also¶
- Explain — per-fn JSON the signatures are derived from
- Context — pairs nicely with
difffor review bots - MCP Server — exposes
diffas an MCP tool over JSON-RPC