Images, binaries, URLs, the clipboard, a deployed site, a directory over SSH. One command, and the same two answers for all of them: do they differ, and where. It never opens a window.
brew install · no dependencies
MIT licensed — free
$ mrdiff before.png after.png Images differ — 12,481 of 390,000 pixels (3.2%) First difference at (412, 88) largest per-channel gap: 41 — --tolerance=41 would call these the same $ mrdiff firmware-v1.bin firmware-v2.bin Binary files differ — 47 regions, first at 0x1A3F 158 of 2,000,000 bytes differ $ mrdiff --ssh ./site deploy@host:/var/www changed config/app.yml only local (not deployed) pages/new.html only on remote (left over?) backups/customers.sql 1 changed, 1 only local, 1 only on remote
An image, a binary, a server — the answer has the same shape. In the terminal you are already in.
Do these differ, and where?
Those two, and on purpose nothing more. MrDiff does not show a difference. In return it answers the same question, in the same shape, for text, an image, a binary, a live site, or a 10 GB log where you only care whether anything moved — so there is no separate tool to learn per kind of file.
Which comparison runs is decided by content, not by extension. A PNG named .bin is still an image.
Do they differ, what fraction of pixels, and where the first one is. Pixels are counted, so a one-pixel change is never rounded to “0.0%”. --tolerance=N lets small value drift count as the same; when it is on, the output says so — “identical” on its own always means byte-identical.
$ mrdiff --tolerance=2 a.png b.jpg Images differ — 1,560 of 390,000 pixels (0.4%) compared with ±2 per channel largest per-channel gap: 5 — --tolerance=5 would call these the same
How many regions differ and the offset of the first, in hex. Adjacent bytes are one region, not forty. If the lengths differ, the common part is compared and the rest is reported as the rest — an inserted byte does not turn into “everything after it changed”.
$ mrdiff v1.bin v2.bin Binary files differ — 47 regions, first at 0x6BDF 158 of 2,000,000 bytes differ
A line diff with a character-level highlight, so the one character that moved is marked — painting the whole line red is not enough. Two line-number columns, left and right: a deleted line 7 and an added line 7 are not the same line.
mrdiff https://a https://b fetches both and compares what the server returned. --clipboard compares what you just copied — text, or a screenshot if there is no text — against a file or a URL. “Is this the same as before?” without saving anything first.
--site walks the git-tracked files in a folder, fetches each one from the live site, and tells you what does not match: changed, or not on the site at all. git ls-files is what defines “the files that are meant to be there”, so build junk and .DS_Store are not yours. “Did I forget to deploy this?” becomes a CI check.
$ mrdiff --site https://example.com ./site changed index.html not on site pricing.html 1 changed, 1 not deployed, 0 could not be checked note: files on the site that are not in git cannot be found this way (HTTP has no directory listing)
host:/path works as a file; --ssh walks a whole tree, both ways. So it reports the third thing a deploy check usually cannot: a file on the server that is not in your repo — an old export, a forgotten backup. Authentication is your ssh's: keys, ~/.ssh/config, agent. It never asks for a password.
--exit-codeExit 1 if they differ. Screenshot regression, firmware build comparison, “did the generated file change” — the cases where you do not need to see the difference, only to know there is one.
--jsonMachine-readable, and never translated — a script that greps the output keeps working whatever the user's language is.
To say two files are identical, both have to be read to the end — no hashing avoids that. What is avoided is the byte-by-byte pass inside blocks that already match: a 1 GB file with a 5-byte change costs about the same as one that is identical (4.3 s cold, 0.1 s from cache, M4 Max).
No account, no telemetry, no server. URLs are fetched without a cache so you are never comparing a stale copy; the request identifies itself as mrdiff.
Every one of these is printed by the tool itself, every time, rather than left for you to discover.
It answers whether and where. Seeing how two images or two firmware images differ is a different tool's job, and MrDiff does not pretend to be one.
No JavaScript is run and nothing is rendered, so the answer can differ from what a browser shows. Pretending otherwise would make a tool that claims differences where there are none.
Line by line. Reformatting counts as a change. Saying that **bold** removed, or keys reordered, is “the same” is outside what this version does.
--site cannot see files that are not in gitHTTP has no directory listing. A left-over customers.xlsx on the server is invisible to it, and it says so on every run rather than implying the site is clean. Over SSH the remote side can be listed, so there it is reported.
One brew install. Nothing else to set up.
$ brew install mr-tabata/tap/mrdiff # then $ mrdiff a.png b.png $ mrdiff a.bin b.bin $ mrdiff https://example.com/a https://example.com/b $ mrdiff --clipboard notes.md $ mrdiff --site https://example.com ./site $ mrdiff local.conf host:/etc/app.conf $ mrdiff --ssh ./site host:/var/www $ mrdiff --exit-code a.png b.png # exit 1 if they differ $ mrdiff --json a.bin b.bin # machine readable
macOS. Written in Swift; image decoding uses the system's ImageIO, which is why it is macOS only.
Output is English by default; MRDIFF_LANG=ja switches the human-readable lines to Japanese. JSON and exit codes never change.