A small, fast CLI to compute the diff between two RDF files. The output is itself an RDF dataset — a TriG or N-Quads document containing two named graphs:
- the named graph for file A holds triples present in A but not in B;
- the named graph for file B holds triples present in B but not in A.
Triples that appear in both files are omitted (they are the "common core").
- Streaming. File B is streamed; only file A is loaded into memory (as a hash
set of triples), so peak memory is
O(|A|). - Multiple input formats auto-detected from the extension:
N-Triples (
.nt), Turtle (.ttl), RDF/XML (.rdf,.owl,.xml), TriG (.trig), N-Quads (.nq). - Transparent gzip for any input ending in
.gz. - Two output formats: TriG (default) and N-Quads.
- Filename-derived graph IRIs (
urn:rdf-compare:source:<basename>) with automatic:1/:2disambiguation when both files share a basename. - CI mode (
--ci) exits non-zero when any difference is found. - Blank-node aware. When either input contains blank nodes, both sides are
canonicalised with the W3C RDFC-1.0
algorithm before the set-diff, so isomorphic sub-graphs cancel regardless of
the bnode labels used. Pass
--ignore-blank-nodesto opt out and skip every triple touching a bnode instead. - Quad-aware. N-Quads and TriG inputs preserve their named graphs; the diff is then written as two parallel files (one per side) since RDF cannot nest named graphs.
- Web viewer (
--view/servesubcommand) — explore the diff in an interactive browser UI with filtering, sorting, prefix-shortened IRIs, and a Leaflet map forgeo:wktLiteralcells.
From source (requires a stable Rust toolchain):
cargo install --path .Or, once published:
cargo install rdf-comparerdf-compare <FILE_A> <FILE_B> [OPTIONS]# Diff two Turtle files, write TriG to stdout
rdf-compare a.ttl b.ttl
# Cross-format diff, write N-Quads to a file
rdf-compare snapshot-old.nt.gz snapshot-new.ttl \
--output-format nq -o diff.nq
# Use in CI: exit 1 when files differ
rdf-compare expected.ttl actual.ttl --ci --quiet -o /dev/nullOpen the result in a browser immediately after diffing:
rdf-compare a.ttl b.ttl --viewOr start an interactive server (files can be selected in the browser):
# Empty viewer — load files interactively in the browser
rdf-compare serve
# Pre-load two source files
rdf-compare serve --file-a a.ttl --file-b b.ttl
# Pre-load a previously saved diff file
rdf-compare serve --diff diff.trig
# Bind to a fixed port and skip auto-opening the browser
rdf-compare serve --file-a a.ttl --file-b b.ttl --bind 127.0.0.1:8080 --no-openThe viewer runs entirely offline — all assets (Tabulator, Leaflet, wellknown) are bundled inside the binary.
| Flag | Description |
|---|---|
--format-a <FMT> |
Force input format for file A (nt, ttl, rdf/xml, trig, nq). |
--format-b <FMT> |
Force input format for file B. |
-o, --output <FILE> |
Write output to FILE instead of stdout. |
--output-format <FMT> |
trig (default) or nq. |
--graph-a <IRI> |
Override the named-graph IRI for "only-in-A" triples. |
--graph-b <IRI> |
Override the named-graph IRI for "only-in-B" triples. |
--quiet |
Suppress the summary line on stderr. |
--ci |
Exit with code 1 if any differences are found. |
--view |
Open the diff in the local web viewer after computing it. |
--no-open |
Do not auto-open the system browser (implies --view). |
--bind <ADDR> |
Bind address for the viewer (default: 127.0.0.1:0). |
--ignore-blank-nodes |
Skip every triple touching a blank node instead of canonicalising. |
| Flag | Description |
|---|---|
--file-a <FILE> |
First RDF file to pre-load (requires --file-b). |
--file-b <FILE> |
Second RDF file to pre-load (requires --file-a). |
--format-a <FMT> |
Force input format for file A. |
--format-b <FMT> |
Force input format for file B. |
--diff <FILE> |
Pre-load a saved diff file instead of recomputing (conflicts with --file-a/--file-b). |
--graph-a <IRI> |
Override the named-graph IRI for the A side. |
--graph-b <IRI> |
Override the named-graph IRI for the B side. |
--bind <ADDR> |
Bind address (default: 127.0.0.1:0). |
--no-open |
Do not auto-open the system browser. |
| Code | Meaning |
|---|---|
0 |
Success (or, without --ci, completed even if differences exist). |
1 |
--ci was set and at least one difference was found. |
2 |
Error (I/O, parse, etc.). |
Given:
# a.ttl
@prefix ex: <http://example.org/> .
ex:s1 ex:p "v1" .
ex:s3 ex:p "vA" .# b.ttl
@prefix ex: <http://example.org/> .
ex:s1 ex:p "v1" .
ex:s3 ex:p "vB" .
ex:s4 ex:p "v4" .rdf-compare a.ttl b.ttl produces:
<urn:rdf-compare:source:a> {
<http://example.org/s3> <http://example.org/p> "vA" .
}
<urn:rdf-compare:source:b> {
<http://example.org/s3> <http://example.org/p> "vB" .
<http://example.org/s4> <http://example.org/p> "v4" .
}
…with a summary on stderr:
A: a.ttl triples=2 only-in-A=1 skipped-bnodes=0
B: b.ttl triples=3 only-in-B=2 skipped-bnodes=0
common=1
- Each input is parsed into a quad stream (triple inputs are tagged with the default graph).
- If either side contains blank nodes, both sides are independently canonicalised using W3C RDFC-1.0 so that isomorphic blank-node structures receive identical canonical labels.
- A symmetric set-diff yields the only-in-A and only-in-B quad sets.
- For triple-only inputs, the two sides are written into a single TriG / N-Quads
file under per-side wrapper graph IRIs. For quad inputs, the original named
graphs are preserved and the result is split across two parallel files
(
<output>-a.<ext>and<output>-b.<ext>) — quad inputs therefore require--output.
With --ignore-blank-nodes, step 2 is skipped and every triple touching a
blank node is dropped before the set-diff; the counts appear as
skipped-bnodes in the summary.
cargo test
cargo clippy --all-targets -- -D warnings
cargo fmtApache-2.0.
