Semantic Splinter

Diff and merge beyond text.

Format-aware diff, three-way merge, and patch for files that line-based tools handle badly.

One binary, splinter, that diffs and merges images, archives, structured data, PDFs, databases, and 3-D meshes. It runs on its own and plugs into Git as a textconv, diff driver, and merge driver. C++20, built with CMake and vcpkg. MIT licensed.

Pre-release and in testing. Every v1 format diffs and merges; the binary releases are not out yet, so you build from source. On text, it reproduces GNU diff, diff3, and git merge-file byte for byte on the golden corpus.

Go to the repository

Build it

No binaries yet, so clone the repository and build from source. The steps and toolchain are in BUILDING.md.

git clone https://github.com/AlphaPixel/SemanticSplinter.git
# or, with the GitHub CLI
gh repo clone AlphaPixel/SemanticSplinter

The tool lands at build/<preset>/src/cli/Release/splinter (splinter.exe on Windows). splinter --version prints the version to quote in a bug report.

Why it exists

Change one pixel in a PNG and most of its compressed bytes change too. Git reports "Binary files differ" and cannot merge two artists editing different parts of the same image. Splinter parses each format into its real units, pixels, chunks, archive members, XML elements, JSON keys, table rows, PDF objects, diffs and merges those units, and writes a valid file back.

Untouched units are copied byte for byte, so Git deltas stay small. Sizes, offsets, CRCs, and checksums are recomputed for you, and compressed streams are compared by content, so recompression noise never shows up as a change. When two sides edit the same unit it reports a conflict with a stable ID and stops. It does not average, and it does not guess.

splinter a.png b.png behaves like diff, splinter -m like diff3 -m, and splinter -P like patch. Exit codes match whichever tool it is standing in for.

Using it

splinter old.png new.png                  # changed regions, metrics, metadata
splinter -u old.png new.png               # unified diff over canonical dump lines
splinter --json=report.json a.png b.png   # machine-readable report
splinter -m mine.png base.png theirs.png  # three-way merge, as diff3 -m
splinter -m mine.zip base.zip theirs.zip  # archive members merged by path, each by its format
splinter -m mine.db  base.db  theirs.db   # SQLite rows by key, cells merged
splinter --binary a.png b.png > logo.patch  # a patch you can apply later
splinter -P < logo.patch                  # apply it, as patch
splinter git-setup                        # wire it in as a Git diff and merge driver

What it handles

Everything below diffs and merges today. The checkmarks show where patch output exists; per-format limits live in the format documents.

FormatDiffMergePatchDocument
Textyesyesyestext
Unknown binaryyesopt-innobinary
ZIP, TAR (gz/zst/xz/bz2)yesyesyescontainers
XMLyesyesyesxml
JSON, JSONCyesyesyesjson
CSV, TSVyesyesnocsv
SVG, SVGZyesyesnosvg
PNGyesyesyespng
JPEGyesyesnojpeg
TIFF, GIF, WebP, BMP, TGA, QOIyesyesnoraster-misc
SQLiteyesyesnosqlite
PDFyespartialnopdf
OBJ/MTL, PLY, STLyesyesnomesh

PDF merges metadata, annotations, and form values but not page content yet. Rendering and page-content merge, plus signature verification and re-signing, come after v1.

Read more