インデントに悩まされずに2つのYAMLファイルを比較する方法

2つのYAMLファイルを比較する最速の方法は、両方をサイドバイサイドのdiffツールに貼り付け、 インデントを正規化して、ハイライトされた行を読むことです。比較そのものは簡単な部分です。 YAMLではノイズが通常より厄介で、紛れ込んだタブ、並び替えられたキー、誰かが引用符で囲んだ値によって、 同じデータを読み込む2つのファイルがまるで共通点がないように見えてしまいます。

このガイドでは、クリーンで信頼できるYAMLのdiffを取得する方法を解説します。 等価な2つのファイルが紙の上で乖離する理由、知っておく価値のある手法、 そして実際に試せる実例を紹介します。ツールだけが必要な場合は、 YAML比較ページでブラウザ上のすべてを処理できます。

YAMLファイルの比較が意外と難しい理由

YAMLは空白に敏感な形式であり(YAML 1.2.2仕様を参照)、 それがまさにdiffを厄介にする原因です。インデントには意味がありますが、 インデントのには意味がありません。一貫していれば問題ないのです。 そのため、2スペースでインデントされたファイルと4スペースのファイルが同一の構造を読み込んでいても、 テキストdiffにはすべての行が違って見えます。

覚えておくべき重要な事実があります。YAMLのマッピングはキー/値のペアの集合であり、 JSONオブジェクトと同様、それらのキーの順序はデータを変えません。つまり、これ:

name: Ada Lovelace
role: editor

と、これ:

role: editor
name: Ada Lovelace

は同じマッピングを読み込みますが、行ベースのdiffでは赤と緑に塗られます。 一方、シーケンスの要素は順序付きなので、リストを並び替えることは本物の変更です。

変更に見えるが、たいていそうではないもの
diffで見えるもの本当の変更か?対処法
2スペースvs 4スペースのインデントいいえ、一貫性だけが重要両方を同じ幅に再フォーマットする
マッピングのキーが異なる順序いいえ、マッピングは順序なし両側のキーをソートする
true vs "true"はい、boolean vs 文字列調査が必要、これは本物
name: Ada vs name: "Ada"いいえ、同じ文字列値引用符を正規化する
フロースタイル [a, b] vs ブロックリストいいえ、同じシーケンス両方に1つのスタイルを選ぶ
シーケンスの要素が異なる順序はい、シーケンスは順序付き調査が必要、これは本物

この2行は本物の落とし穴です。引用符のないtrueはbooleanですが、 引用符付きでは文字列"true"であり、その区別は実際の障害を引き起こしてきました。 しかしname: Adaname: "Ada"は同じ文字列です。 パーサーがこれをどう解決するかの詳細は、仕様の core schema に関するセクションが参考になります。

YAMLを比較する4つの方法とその使い分け

唯一の最良の方法はありません。ファイルがどこにあるか、何を調べたいかによって異なります。 よく使われる方法を比較します。

方法向いている場面手間YAMLを理解するか?
目視確認小さなファイル、1〜2キーいいえ、あなたがパーサー
オンラインdiffツールクイックチェック、どこからでも貼り付け可能再フォーマットすれば対応
コマンドライン(yqディスク上のファイル、スクリプティング、キーのソート最初にソートすれば対応
IDEまたはgit diffリポジトリ内のファイルコミット済みなら低デフォルトは行ベース

ほとんどの人にとって、ブラウザツールはインストール不要で、Kubernetesマニフェストや CI設定からスニペットを直接貼り付けられるため、速度面で優れています。問題はフォーマットのノイズで、 次で対処します。ターミナルで作業する場合は、yq が学ぶべきツールで、jqがJSONに対して行うのと同じようにキーをソートできます。

最速のクリーン比較、ステップバイステップ

誰かが2つのマニフェストを渡して「何が違うの?」と聞いてきたときに使うルーティンです。 約15秒で完了します。

  1. YAML比較ツールを開く。
  2. 左に元のファイル、右に新しいバージョンを貼り付ける。
  3. 両側でフォーマットをクリックして、同じインデントを共有するようにする。
  4. キーのソートをオンにして、並び替えられたマッピングのキーが変更として表示されないようにする。
  5. 結果を読む。緑が追加、赤が削除、変更された値はそれぞれ1つずつ表示される。

ステップ3と4がポイントです。両方のファイルが同じインデントを使用し、 キーがソートされると、ハイライトされるのは実際に変更された内容だけになります。 diffエンジンはGoogleの diff-match-patch を基盤としており、最初に行単位で比較するため、長いファイルでも高速に動作します。

実例

あるユーザーレコードへの変更をレビューしているとします。変更前:

name: Ada Lovelace
role: editor
active: true
seats: 3

そして、チームメイトから受け取った変更後:

active: true
name: Ada Lovelace
role: admin
seats: 5
team: platform

これをプレーンな行ベースdiffに入力すると、キーの順序が違うためほぼすべての行が移動したように見えます。 両方を再フォーマットしてソートすると、実際の話は短くなります:

実際に変更されたもの
キー変更前変更後変更
roleeditoradmin変更
seats35変更
teamplatform追加
nameAda LovelaceAda Lovelace変更なし
activetruetrue変更なし(移動のみ)

本物の変更は3つ:ロールの昇格、シート数、新しいチームキー。 並び替えはノイズでした。editorからadminへの昇格は レビューで捉えたい変更ですが、誤検知に埋もれていると見逃しやすいです。

コマンドラインでフォーマットのノイズを除去する

ファイルがすでにディスクにある場合、同じ「再フォーマットとソート」のアイデアを 2つの短いコマンドで実現できます。yqはキーをソートして正規形を再出力できるので、 その後のプレーンdiffが正確になります:

yq -P 'sort_keys(..)' old.yaml > old.sorted.yaml
yq -P 'sort_keys(..)' new.yaml > new.sorted.yaml
diff old.sorted.yaml new.sorted.yaml

これでdiffは本当に変更された値のみを報告します。両ファイルが同じインデントと 同じキー順序を持つためです。これはブラウザでフォーマットとキーのソートをクリックする ターミナル版です。

YAML特有の落とし穴

いくつかのYAMLの機能は人を驚かせるdiffを引き起こします。anchorsとaliases (&name*name)により、一方のファイルが参照で値を繰り返し、 もう一方がそれを完全に書き出すことができます。両方とも同じデータを読み込みますが、 まったく違って読めます。悪名高い「ノルウェー問題」も別の例です。引用符のない nooffyesは古いYAML 1.1パーサーでbooleanとして パースされる可能性があり、country: NOfalseになることがあります。 YAML 1.2はスキーマを修正しましたが、多くのツールが今も1.1の動作を出荷しています。 値が型を変えたように見えたら、まずそれを確認します。

注意すべき一般的な落とし穴

落とし穴なぜ問題になるか対処法
インデントにタブYAMLはインデントにタブを禁止している。ファイルがパースさえできないことがあるまずタブをスペースに変換する
引用符あり vs なしのスカラー"true"は文字列、trueはbooleanこれは本物の変更の可能性がある、無視しないこと
anchorsとaliases一方のファイルは値をインラインに、もう一方は参照するanchorsを解決してから展開形を比較する
ノルウェー問題noはYAML 1.1でfalseとしてパースされることがある曖昧な文字列に引用符を付ける。パーサーのバージョンを確認する
行末の空白値の後の見えない空白が変更として表示される比較前に行末の空白をトリムする

YAMLとJSONは見た目より近い

YAML 1.2はJSONのスーパーセットなので、すべてのJSONドキュメントは有効なYAMLです。 これは比較に便利です。インデントやanchorsに苦戦している場合は、両方のファイルをJSONに変換し、 同じ形式にフォーマットして、それをdiffすればよいのです。 PyYAML を含む多くのパーサーは、YAMLをプレーンなデータ構造にラウンドトリップでき、それをJSONとして再出力できます。 これによりスタイルの違いが取り除かれ、データだけが残ります。

関連ツール

YAMLだけを扱うことはほとんどありません。同じデータのJSON形式を比較するなら、 JSON比較が同じアイデアを適用します。 環境設定と.envファイルは 設定比較ページでうまく並びます。 2つのAPI呼び出し間の変更をレビューするのは APIレスポンスdiffのためのものです。

よくある質問

YAMLファイルをオンラインで比較するとどこかにアップロードされますか?
comparetext.orgではdiffはブラウザで実行されます。2つのYAMLファイルはあなた自身のマシン上のJavaScriptによって比較されるため、明示的に保存または共有をクリックしない限り、サーバーには何も送信されません。Kubernetesマニフェスト、CI設定、キーストロークのたびにアップロードするサイトに貼り付けたくないデータにも安全です。
2つのYAMLファイルがすべての行で差異があると表示されるのはなぜですか?
ほぼ常にフォーマットの問題であり、本物の変更ではありません。一方のファイルが2スペース、もう一方が4スペースでインデントされている、あるいはマッピングのキーの順序が異なる、または一方が引用符を使い他方が使わない、といったことです。両側を同じインデントに再フォーマットし、キーをソートして順序が問題にならないようにします。その後、diffは本当に変更された少数の値に縮小されます。
YAMLを比較するときインデント幅は重要ですか?
意味の上では重要ではなく、一貫性のためだけです。YAMLは構造を示すためにインデントを使いますが、ファイル内で各レベルが一貫している限り、2スペースか4スペースかは気にしません。そのため、2スペースのファイルと4スペースのファイルが同一のデータを保持していても、テキストdiffにはまったく違って見えます。両方を同じ幅に再フォーマットすると、その偽の差異が取り除かれます。ただしタブはインデントにまったく許可されていません。
キー順序を無視してYAMLを比較するにはどうすればよいですか?
YAMLのマッピングのキーには順序がないため、同じキーを異なる順序で持つ2つのファイルは同じデータを保持します。テキストdiffを一致させるには、比較前に両側のキーをソートします。ブラウザではキーのソート(正規化)オプションを使用します。コマンドラインでは、yqがsort_keysで実行します。両ファイルのキーが同じソート順になると、本物の値の変更のみが表示されます。シーケンスの要素は順序を保つので、それらはソートしないでください。
YAMLのノルウェー問題とは何ですか?
引用符のない値NOが文字列"NO"ではなくboolean falseとしてパースされる、YAMLの古典的な落とし穴です。そのためノルウェーの国コードがfalseになってしまいます。これはyes、no、on、offをbooleanとして扱うYAML 1.1パーサーで起こります。YAML 1.2はルールを狭めましたが、多くのツールが今も1.1の動作を出荷しています。diffで値が単語からtrueまたはfalseに変わったように見えたら、引用符のないboolean風の文字列が原因の可能性が高いです。値を引用符で囲めば直ります。
ページがフリーズせずに大きなYAMLファイルを比較できますか?
はい、ある程度まで。行モードdiffは、各文字ではなくまず行全体を比較するため、数千行のファイルでも高速です。非常に大きなファイル(数メガバイト)は、データをストリームするyqやgit diffなどのコマンドラインツールで処理する方が適しています。ブラウザで快適にスクロールできるものなら、オンラインdiffが速い選択肢です。

試してみませんか?YAML比較ツールにファイルを貼り付けて、何が変わったか確認しましょう。