日本語 ▾ トピック ▾ 最新バージョン ▾ gitdiffcore 最終更新日: 2.44.0

名前

gitdiffcore - diff 出力の微調整

概要

git diff *

説明

diff コマンドの git diff-indexgit diff-files、および git diff-tree は、diff 出力を表示する前に、見つかった差分を型破りな方法で操作するように指示できます。この操作は総称して「diffcore 変換」と呼ばれます。この短いメモは、それらが何であるか、そして従来の方法よりも理解しやすい diff 出力を生成するためにそれらを使用する方法を説明します。

操作の連鎖

git diff-* ファミリーは、まず2つのファイルのセットを比較することによって動作します。

  • git diff-index は、「tree」オブジェクトの内容と作業ディレクトリ ( --cached フラグが使用されていない場合) または「tree」オブジェクトとインデックスファイル ( --cached フラグが使用されている場合) を比較します。

  • git diff-files は、インデックスファイルの内容と作業ディレクトリを比較します。

  • git diff-tree は、2つの「tree」オブジェクトの内容を比較します。

これらのすべての場合において、コマンド自体はまず、コマンドラインで指定されたパススペックによって2つのファイルのセットを任意に制限し、結果として得られる2つのファイルのセット内の対応するパスを比較します。

パススペックは、diff が動作する世界を制限するために使用されます。これらは、指定されたパス名のセット外のファイルペアを削除します。例: 入力ファイルペアのセットに以下が含まれていた場合

:100644 100644 bcd1234... 0123456... M junkfile

しかし、コマンド呼び出しが git diff-files myfile だった場合、「myfile」のみが考慮されるため、junkfile エントリはリストから削除されます。

比較の結果は、これらのコマンドから、-p オプションが使用されていない場合に出力される形式に似た形式で、内部的に「diffcore」と呼ばれるものに渡されます。例:

in-place edit  :100644 100644 bcd1234... 0123456... M file0
create         :000000 100644 0000000... 1234567... A file4
delete         :100644 000000 1234567... 0000000... D file5
unmerged       :000000 000000 0000000... 0000000... U file6

diffcore メカニズムには、このような比較結果のリスト (この時点ではそれぞれが単一のファイルを扱っていますが、「filepair」と呼ばれます) が与えられ、このようなリストを別のリストに変換します。現在、5つのこのような変換があります。

  • diffcore-break

  • diffcore-rename

  • diffcore-merge-broken

  • diffcore-pickaxe

  • diffcore-order

  • diffcore-rotate

これらは順次適用されます。git diff-* コマンドが見つけるファイルペアのセットは diffcore-break への入力として使用され、diffcore-break からの出力は次の変換への入力として使用されます。最終結果は出力ルーチンに渡され、diff-raw 形式 (git diff-* コマンドのマニュアルの出力形式のセクションを参照) または diff-patch 形式のいずれかを生成します。

diffcore-break: 完全な書き換えを分割するため

チェーンの2番目の変換は diffcore-break であり、git diff-* コマンドの -B オプションによって制御されます。これは、「完全な書き換え」を表すファイルペアを検出し、そのようなファイルペアを削除と作成を表す2つのファイルペアに分割するために使用されます。例: 入力にこのファイルペアが含まれていた場合

:100644 100644 bcd1234... 0123456... M file0

そして、ファイル「file0」が完全に書き換えられていると検出された場合、それは以下のように変更されます。

:100644 000000 bcd1234... 0000000... D file0
:000000 100644 0000000... 0123456... A file0

ファイルペアを分割する目的で、diffcore-break は、変更前後のファイルの内容間の変更の程度を調べます (つまり、上記の例では「bcd1234…​」と「0123456…​」を SHA-1 コンテンツ ID とする内容)。元の内容の削除量と新規挿入量が合計され、それが「ブレークスコア」を超えると、ファイルペアは2つに分割されます。ブレークスコアは、元と結果の小さい方のサイズの50%がデフォルトであり (つまり、編集によってファイルが縮小された場合は結果のサイズが使用され、編集によってファイルが長くなった場合は元のサイズが使用されます)、-B オプションの後に数値を指定することでカスタマイズできます (例: 「-B75」と指定して75%を使用するように指示します)。

diffcore-rename: 名前変更とコピーの検出

この変換は名前変更とコピーを検出するために使用され、git diff-* コマンドの -M オプション (名前変更を検出するため) と -C オプション (コピーも検出するため) によって制御されます。入力にこれらのファイルペアが含まれていた場合

:100644 000000 0123456... 0000000... D fileX
:000000 100644 0000000... 0123456... A file0

そして、削除されたファイル fileX の内容が作成されたファイル file0 の内容と十分に似ている場合、名前変更検出はこれらのファイルペアをマージして以下を作成します。

:100644 100644 0123456... 0123456... R100 fileX file0

「-C」オプションが使用される場合、変更されたファイルの元の内容、および削除されたファイル (「--find-copies-harder」オプションが使用された場合は変更されていないファイルも) は、名前変更/コピー操作におけるソースファイルの候補として考慮されます。入力が、変更されたファイル fileY と新しく作成されたファイル file0 を扱うこれらのファイルペアのようであった場合

:100644 100644 0123456... 1234567... M fileY
:000000 100644 0000000... bcd3456... A file0

fileY の元の内容と file0 の結果の内容が比較され、十分に似ている場合、それらは以下に変更されます。

:100644 100644 0123456... 1234567... M fileY
:100644 100644 0123456... bcd3456... C100 fileY file0

名前変更およびコピー検出の両方において、diffcore-break で使用されるのと同じ「変更の程度」アルゴリズムが、2つのファイルが「十分に似ているか」を判断するために使用され、-M または -C オプションの後に数値を指定することで、デフォルトの50%とは異なる類似度スコアを使用するようにカスタマイズできます (例: 「-M8」と指定して8/10 = 80%を使用するように指示します)。

名前変更検出がオンで、コピー検出とブレーク検出の両方がオフの場合、名前変更検出は、まずファイル名が同じままディレクトリ間でファイルが移動したかどうかをチェックする予備ステップを追加することに注意してください。ディレクトリに追加されたファイルの内容が、異なるディレクトリから削除された同じ名前のファイルと十分に似ている場合、それらは名前変更としてマークされ、後の二次ステップ (すべての未マッチファイルをペアワイズ比較して、最高のコンテンツ類似度によって決定される「最適な」マッチを見つけるステップ) から除外されます。したがって、たとえば、削除された docs/ext.txt と追加された docs/config/ext.txt が十分に似ている場合、それらは名前変更としてマークされ、削除された docs/ext.txt にさらに似ている可能性のある追加された docs/ext.md が後のステップで名前変更の宛先として考慮されるのを防ぎます。このため、予備的な「同じファイル名の一致」ステップは、ファイルペアを名前変更としてマークし、より良い一致の他の候補を考慮するのをやめるために、少し高いしきい値を使用します。この予備パスでは、ファイルごとに最大1回の比較が行われます。したがって、正確な名前変更検出後、ディレクトリ階層全体にいくつかの残りの ext.txt ファイルがある場合、これらのファイルについてはこの予備ステップがスキップされる可能性があります。

注: --find-copies-harder オプションを -C オプションと併用する場合、git diff-* コマンドは、変更されたファイルペアだけでなく、変更されていないファイルペアも diffcore メカニズムに供給します。これにより、コピー検出器は変更されていないファイルをコピー元の候補として考慮できますが、速度が低下します。--find-copies-harder がない場合、git diff-* コマンドは、コピーされたファイルが同じチェンジセットで変更された場合にのみコピーを検出できます。

diffcore-merge-broken: 完全な書き換えを元に戻す

この変換は、diffcore-break によって分割され、diffcore-rename によって名前変更/コピーに変換されなかったファイルペアを、単一の変更にマージするために使用されます。これは diffcore-break が使用されるときに常に実行されます。

分割されたファイルペアを元に戻す目的で、diffcore-break と diffcore-rename で使用されるものとは異なる「変更の程度」の計算を使用します。元のものからの削除のみをカウントし、挿入はカウントしません。100行のドキュメントから10行だけを削除し、910行の新しい行を追加して新しい1000行のドキュメントを作成したとしても、完全な書き換えを行ったことにはなりません。diffcore-break は、このようなケースを分割して、diffcore-rename がそのようなファイルペアを名前変更/コピー検出の候補として考慮できるようにしますが、そのように分割されたファイルペアが他のファイルペアと一致して名前変更/コピーを作成しなかった場合、この変換はそれらを元の「変更」にマージし直します。

「変更の程度」パラメータは、デフォルトの80% (つまり、元の資料の80%以上が削除されない限り、分割されたペアは単一の変更にマージされます) から、-B オプションに2番目の数値を指定することで調整できます。例:

  • -B50/60 (diffcore-break に50%の「ブレークスコア」を与え、diffcore-merge-broken に60%を使用します)。

  • -B/60 (上記と同じ。diffcore-break はデフォルトで50%のため)。

以前の実装では、分割されたペアが個別の作成と削除のパッチとして残されていたことに注意してください。これは不要なハックであり、最新の実装では常にすべての分割されたペアが変更にマージされますが、そのような完全な書き換えの場合には、レビューを容易にするために、古いバージョンの内容全体に - がプレフィックスされ、その後に新しいバージョンの内容全体に + がプレフィックスされて、結果のパッチ出力は異なる形式で出力されます。

diffcore-pickaxe: 指定された文字列の追加/削除の検出

この変換は、指定された文字列をプリイメージとポストイメージの間で特定の方法で変更するファイルペアのセットに限定します。-S<block-of-text> と -G<regular-expression> オプションは、これらの文字列が検索される異なる方法を指定するために使用されます。

「-S<block-of-text>」は、プリイメージとポストイメージに指定されたテキストブロックの出現回数が異なるファイルペアを検出します。定義上、ファイル内の移動は検出しません。また、チェンジセットが関心のある文字列に影響を与えずにファイルを一括移動する場合、diffcore-rename が通常どおりに起動し、-S はファイルペアを省略します (その名前変更検出されたファイルペア内でその文字列の出現回数が変化しなかったため)。--pickaxe-regex とともに使用すると、<block-of-text> をリテラル文字列ではなく、拡張 POSIX 正規表現として一致させます。

「-G<regular-expression>」(記憶しやすい: grep) は、テキスト diff に指定された正規表現に一致する追加または削除された行があるファイルペアを検出します。これは、ファイル内 (または名前変更検出が同じファイルと見なすもの) の移動を検出することを意味しますが、これはノイズです。実装は diff を2回実行して grep するため、非常にコストがかかる場合があります。速度を上げるために、textconv フィルターのないバイナリファイルは無視されます。

-S または -G--pickaxe-all なしで使用される場合、それぞれの基準に一致するファイルペアのみが出力に残されます。--pickaxe-all が使用される場合、チェンジセット内の1つのファイルペアでもそれぞれの基準に一致すれば、チェンジセット全体が残されます。この動作は、チェンジセット全体のコンテキストで変更をレビューしやすくするように設計されています。

diffcore-order: ファイル名に基づいて出力を並べ替える

これは、ユーザー (またはプロジェクト) の好みに従ってファイルペアを並べ替えるために使用され、git diff-* コマンドの -O オプションによって制御されます。

これは、各行がシェルグロブパターンであるテキストファイルを受け取ります。ファイル内の前の行のグロブパターンに一致するファイルペアは、後の行に一致するファイルペアの前に出力され、どのグロブパターンにも一致しないファイルペアは最後に出力されます。

例として、コア Git の典型的なオーダーファイルは次のようになるでしょう。

README
Makefile
Documentation
*.h
*.c
t

diffcore-rotate: 出力開始パスの変更

この変換は1つのパス名を受け取り、ファイルペアのセットを回転させて、指定されたパス名のファイルペアが最初に来るようにし、必要に応じてその前に来るパスを破棄します。これは --skip-to および --rotate-to オプションを実装するために使用されます。指定されたパス名がファイルペアのセットにない場合はエラーですが、「git log」ファミリーのコマンドと一緒に使用する場合は、与えられたパスが「git log」コマンドによって表示される各コミットによって変更されることを期待するのは不合理であるため、エラーを出力するのは有用ではありません。このため、「git log」と一緒に使用される場合、指定されたパス名と同じ、またはその後にソートされる最初のファイルペアから出力が開始されます。

この変換を diffcore-order と組み合わせて使用すると、予期しない結果が生成されます。diffcore-order が有効な場合、この変換への入力はソートされていない可能性が高いためです。

GIT

git[1]スイートの一部

scroll-to-top