
2026/04/12 3:07
# Git Diff ドライバーの構築方法 ## 概要 **Diff ドライバー**とは、2 つのファイルを改行単位で比較する実際の処理を実行するプログラムです。Git がコミット間やブランチ間の差分を表示する際、内部でこれらのドライバーを利用します。最も一般的なビルトインのドライバーは `diff` ですが、開発者はバイナリファイルに対応した独自のドライバーや、特定の符号化方式・カスタム出力形式を扱うドライバーなどを記述することも可能です。 ## カスタム Diff ドライバーの作成方法 ### 1. Git の内部 Diff 構造を理解する Git の差分処理は以下のステップで構成されています: 1. **Diff ヘルパー**: データを準備し、標準入力(stdin)経由でドライバーにコールを行います。 2. **ドライバープログラム**: 標準入力を読み込み、処理を実行し、特定形式の差分結果を標準出力(stdout)に出力します。 3. **Post-Diff プロセス**: ドライバーからの出力を解析して、最終的な表示を行っていきます。 ### 2. ドライバーの入力・出力形式を定義する Git は、ドライバーに対し以下のような入力を期待しています: - `a/path/to/file`:「旧」ファイル(前バージョン)のパス - `b/path/to/file`:「新」ファイル(現在のバージョン)のパス - `file mode`: ファイルの権限および種類 また、Git が解釈可能な形式の一つで差分結果を出力する必要があります: - Unified diff (`@@hunk-header...@@`) — ソースコードにおけるデフォルト形式 - Context diff(旧形式で現在は稀に使用) ### 3. 基本となるドライバースクリプトを作成する 以下は、基本的な Diff ドライバーとして動作する簡単な Bash スクリプトの例です: ```bash #!/bin/bash # 標準入力から旧・新ファイルのパスを読み出す old_path=$(sed -n '/^a\/\|^b\/$/p' | head -1) # シンプルな抽出(本番環境用ではありません) new_path=$(sed -n '/^a\/\|^b\/$/p' | tail -1) # これ以降に独自のロジックを実行するか、diff, colordiff などの外部ツールを呼び出すことができます diff -u "$old_path" "$new_path" > /dev/null || true exit 0 ``` > ⚠️ 注意:これは簡易的な例です。実際のドライバーでは、Git のバイナリ入力形式を正確に解析し、オプションに対応させる必要があります。 ### 4. ドライバーのテスト Git はドライバー名を使用してスクリプトを検出するため、以下の設定が必要です: - 名称が明確である(例:`mycustomdiff`) - エキイカット権限を持つ(`chmod +x mycustomdiff`) - `$GIT_DIR/hooks/` または必要に応じてシステムパス上に配置される ### 5. Git への登録 ドライバー名を Git に登録するには、以下のように設定を行います: ```bash git config --global diff.mydriver.path "/path/to/mycustomdiff" git config --global diff.mydriver.binary false # バイナリファイルを特別に扱わない場合 ``` alternatively 、カスタムファイルタイプを定義することも可能です: ```bash git config core.fileType "text/diff+myformat" git config core.editor myeditor # その他の目的の例 ``` ### 6. 実際の利用 ドライバーを登録した後、特定のタイプのファイルを差分表示する際に使用できます: ```bash git diff --binary=mydriver filename.ext ``` あるいは、ファイルタイプごとに設定することも可能です: ```bash git config diff.mydriver.binary false git add --intent-to-add "*.ext" # バイナリ処理のための例 ``` ## ヒントとベストプラクティス - Git との通信には、常に適切な stdin/stdout パイプを使用してください。 - 境界ケース(ファイルが見つからない場合、権限エラーなど)に対応してください。 - ロジックの参考に `diff`, `unifdef`, `patch` などのツールを利用してください。 - Unix/macOS および Windows(WSL/Cygwin を通じて)、複数プラットフォームで十分にテストしてください。 ## まとめ Diff ドライバーの記述により、Git がどのように差分を表示するかを完全に制御できます。カスタムフォーマットの実装、外部ツールとの連携、あるいはパフォーマンス最適化などの目的にせよ、本ガイドは開始するための基礎を提供します。
RSS: https://news.ycombinator.com/rss
要約▶
日本語翻訳:
Jamie Tannaon による 2026 年 4 月 11 日の記事で、標準のテキスト変換では不十分な場合に
git diff のカスタム外部コマンドを作成する方法について説明されています。動機は、部分文書化のある Git Diffs man ページへの言及がありながら、実際の実装において「renovate-packagedata-diff」を実装する際に生じました。さらに、Andrew Nesbitt 氏の「Git Diff Drivers」に関する投稿や OpenAPI 仕様のための「oasdiff」との作業の影響を受けました。外部コマンドは正確に 7 つの引数を受け取ります:ファイル名;"before"/"after" コンテンツへのパス(引数 2 および 5);"before"/"after" ファイルの SHA-1 ハッシュ値(引数 3 および 6);および"before"/"after" ファイル用のオクタルモード(引数 4 および 7)。新しいファイル作成または削除において欠けているコンテンツは /dev/null を介して処理され、使用されない引数は単一のドット (.) として渡されます。oasdiff を使用する例のスクリプトは、追加/削除されたファイルを扱うために null コンテンツを検査し、カラフルな変更ログを生成しますが、権限の変更はまだ対応していません。将来の拡張としては、SHA-1 チェックサムを取り入れて diff キャッシュの効率性を向上させることが可能です。本文
ジェイミー・タナオン(Jamie Tannaon)氏による、2026 年 4 月 11 日の投稿です(CC-BY-NC-SA-4.0 ライセンスおよび Apache License Version 2.0 のもとで公開されています)。所要時間:約 3 分
私が 2024 年 11 月以来、ずっと書こうと思っていたのは、
git diff を使ってファイル間の差分(diff)を取得するための外部コマンドを作成する方法についてです。以前、「renovate-package-data-diff」を実装している際、そのような実装方法に関するドキュメントが不足していることに気づき、ブログ記事としてまとめる価値があると考えました。(その後、Git Diff の man ページにも関連する説明は存在することに気づきましたが、それなりに見つけにくいものでした。)
これは長らく TODO リストの末尾にありましたが、最近アンドリュー・ネスビット(Andrew Nesbitt)氏の「Git Diff Drivers」に関する優れた投稿に触発され、さらに今週「oasdiff」を使って OpenAPI 仕様の差分比較を検討する機会を得たことから、このテーマについて改めて詳しく解説し、また
oasdiff を別項目として Git ドライブの一例として追加したくなります。
なお、ここでは出力に含まれる情報をより詳細に伝えるケースであり、バイナリファイルを差分表示に適したテキスト形式に変換するための
textconv 方法だけに依存することはできません。多くの場合で textconv が十分すぎるでしょう!
どの引数を処理すればよいか?
多くのツールは、「tool [before] [after]」として実行されると標準的に動作しますが、
git diff は外部に呼び出すコマンドに対して 7 つの引数を渡します。これは驚くべき事実かもしれませんが、より豊かな情報を提供しており、実用上有益です。
具体的な「例を示したままの説明」のために、既存ファイルの更新や新規作成・削除時の挙動を見てみましょう:
既存ファイルを更新する場合
renovate-package-data-diff \ renovate/github-co-cddo-api-catalogue.json # 1: リポジトリ内でのファイル名 /tmp/git-blob-shryRa/github-co-cddo-api-catalogue.json # 2: 「更新前」の内容 f0a1311ae439fff36f994a3be5d5a7eb7d7a34dc # 3: 「更新前」ファイルの SHA-1 ハッシュ 100644 # 4: 「更新前」ファイルのオクタル形式のモード(アクセス権限) /tmp/git-blob-y2mrZp/github-co-cddo-api-catalogue.json # 5: 「更新後」の内容 e39975894a72f706e6a59bccf31120ffaa219ff3 # 6: 「更新後」ファイルの SHA-1 ハッシュ 100644 # 7: 「更新後」ファイルのオクタル形式のモード(アクセス権限)
新規ファイルが作成された場合
renovate-package-data-diff \ renovate/github-co-cddo-api-catalogue.json # 1: リポジトリ内でのファイル名 /dev/null # 2: 「更新前」の内容(存在しないため/dev/null) . # 3: 「更新前」ファイルの SHA-1 ハッシュ(不適用なのでドット「.」で代用) . # 4: 「更新前」ファイルのオクタル形式のモード(不適用のため同上) /tmp/git-blob-iAbnMD/github-co-cddo-api-catalogue.json # 5: 「更新後」の内容 5c55e5b99b21db68c360419d44dac906c336bec6 # 6: 「更新後」ファイルの SHA-1 ハッシュ 100644 # 7: 「更新後」ファイルのオクタル形式のモード(アクセス権限)
ファイルが削除された場合
renovate-package-data-diff \ renovate/github-co-cddo-api-catalogue.json # 1: リポジトリ内でのファイル名 /tmp/git-blob-aBldZ4/github-co-cddo-api-catalogue.json # 2: 「更新前」の内容 6b24e38aa4aac8e00e44ab68e156744138ef6afc # 3: 「更新前」ファイルの SHA-1 ハッシュ 100644 # 4: 「更新前」ファイルのオクタル形式のモード(アクセス権限) /dev/null # 5: 「更新後」の内容(存在しないため/dev/null) . # 6: 「更新後」ファイルの SHA-1 ハッシュ(不適用のためドット「.」で代用) . # 7: 「更新後」ファイルのオクタル形式のモード(不適用のため同上)
注意点として、ファイルが新規作成または削除された際には
/dev/null が使用され、該当しない場合は代わりに .(ドット)が提供されます。
また、コマンドに通常の引数と「git diff 用の引数」を両方扱うように設定したい場合、環境変数
GIT_PAGER_IN_USE が設定されているかどうかも確認しておくのが有益です。
oasdiff を使用した例
別途公開した記事にも触れていますが、上記の情報を基に、OpenAPI 仕様の比較を行うための軽量なラップ済みスクリプトを書くのは容易です。
例えば、最も基本的な実装としては以下のようなものが考えられます:
#!/usr/bin/env bash #参照元: https://www.jvt.me/posts/2026/04/11/oasdiff-driver/ # 'git diff'用のドライブとして、指定されたOpenAPI仕様に人間の読みやすい変更履歴を提供するスクリプトです if [[ "$2" == "/dev/null" ]]; then echo "$1 は追加されました" exit 0 elif [[ "$5" == "/dev/null" ]]; then echo "$1 は削除されました" exit 0 fi # 私は常に色付きの出力を好みます oasdiff changelog "$2" "$5" --color always
この実装ではファイルのアクセス権限(パーミッション)の変更に対応しておらず、これは報告に役立つ情報となり得ます。また、ファイルの SHA-1 チェックサムを使用して差分結果をキャッシュするようにする価値もあるでしょう。