画像もバイナリも URL も、
もちろんテキストも。
違うか、どこが、を答える。

画像・バイナリ・URL・クリップボード・公開中のサイト・SSH 越しのディレクトリ。どれも同じコマンドで、同じ 2 つを答える ── 違うか、どこが。ウィンドウは開かない。

1 つのコマンドで、どの種類でも macOS・brew install 1 回・依存なし MIT ライセンス — 無料
$ 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

画像も、バイナリも、サーバも、同じ形の答え。いま開いているターミナルの中に。

違うか、どこが。

MrDiff が答えるのはこの 2 つで、そう決めてある。差分を見せることはしない。その代わり、テキストでも画像でもバイナリでも、公開中のサイトでも 10 GB のログでも、同じ問いに同じ形で答える。ファイルの種類ごとに道具を覚えなくていい。

1 つのコマンドで、どの種類でも

どの比較を走らせるかは、拡張子ではなく中身で決める。.bin と名付けられた PNG も画像として比べる。

画像

違うか、何画素が、最初はどこか。画素を数えるので、1 画素の違いが「0.0%」に丸まることはない。--tolerance=N で小さな値のずれを同じとみなせる。緩めたときは出力にそう書く — 何も付かない「同じ」は、必ずバイト単位で同じという意味。

$ 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

バイナリ

違う箇所がいくつあり、最初はどのオフセットか(16 進)。隣り合うバイトは 1 箇所と数える。長さが違えば共通部分だけを比べ、余りは余りとして言う — 1 バイトの挿入が「以降ぜんぶ違う」にはならない。

$ mrdiff v1.bin v2.bin
Binary files differ — 47 regions, first at 0x6BDF
  158 of 2,000,000 bytes differ

テキストとソース

行の差分に、文字単位のハイライト。動いた 1 文字に印が付く — 行ごと赤く塗るだけでは足りない。行番号は左右 2 列。消えた 7 行目と増えた 7 行目は同じ行ではない。

URL とクリップボード

mrdiff https://a https://b は両方を取ってきて、サーバが返したものを比べる。--clipboard は、いまコピーしたもの — 文字が無ければスクリーンショット — をファイルや URL と比べる。何も保存せずに「さっきのと同じ?」が聞ける。

公開中のサイトと、元のフォルダ

--site はフォルダの git 管理下のファイルを 1 つずつサイトから取ってきて、合わないものを言う: 違う、またはサイトに無い。「置いてあるべきファイル」を決めるのは git ls-files なので、ビルドの中間物や .DS_Store は数えない。「これ、デプロイし忘れてない?」が CI のチェックになる。

$ 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)

手元のディレクトリとサーバ、SSH 越しに

host:/path はファイルとして使える。--ssh はツリー全体を両方向で見る。だから、デプロイ確認がふつう出せない 3 つ目が出る: サーバにあってリポジトリに無いファイル — 古い書き出し、忘れたバックアップ。認証は手元の ssh のもの: 鍵、~/.ssh/config、agent。パスワードは聞かない。

パイプラインに置く前提で

答えないこと

どれも道具が自分で毎回そう言う。使う人が後で気づく形にはしない。

差分を見せはしない

答えるのは「違うか」と「どこが」。2 枚の画像や 2 つのファームウェアがどう違うかを見るのは別の道具の仕事で、MrDiff はそのふりをしない。

URL は、サーバが返したもの

JavaScript は動かさないし描画もしないので、ブラウザで見える画面と答えが違うことがある。そこをごまかすと、無い違いを「違う」と言う道具になる。

Markdown や JSON は、テキストとして比べる

行の文字列を比べる。整形を変えただけでも違いに数える。**強調** を外した、キーの順番を変えた、といった「意味は同じ」を同じと言うのは、この版の仕事の外。

--site は、git に無いファイルを見られない

HTTP にはディレクトリ一覧が無い。サーバに置き忘れた customers.xlsx はここからは見えず、「サイトはきれい」と読ませないために毎回そう書く。SSH ならリモート側を列挙できるので、そちらでは出る。

インストール

brew install 1 回。ほかに設定するものは無い。

$ brew install mr-tabata/tap/mrdiff

# あとは
$ 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     # 違えば 1 で終わる
$ mrdiff --json a.bin b.bin          # 機械向け

macOS 専用。Swift 製で、画像の読み込みに OS の ImageIO を使うため。 出力は既定で英語。MRDIFF_LANG=ja で人向けの行だけ日本語になる。JSON と終了コードは変わらない。