英語 ▾ トピック ▾ 最新バージョン ▾ gitdiffcore は2.44.0で最終更新されました

名称

gitdiffcore - diff出力の調整

書式

git diff *

説明

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

操作の連鎖

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

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

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

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

これらのすべての場合において、コマンド自体はまず、コマンドラインで与えられたパススペックによって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メカニズムは、このような比較結果のリスト(この時点ではそれぞれが単一のファイルについて言及していますが、それぞれを「ファイルペア」と呼びます)を受け取り、そのリストを別のリストに変換します。現在、そのような変換は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 は、変更前と変更後のファイルの内容間の変更の程度を調べます(上記の例では、SHA-1コンテンツIDとして「bcd1234…​」と「0123456…​」を持つ内容)。元の内容の削除量と新しい内容の挿入量を合計し、それが「ブレークスコア」を超えた場合、ファイルペアは2つに分割されます。ブレークスコアは、元ファイルと結果のファイルのうち小さい方のサイズの50%にデフォルト設定されています(つまり、編集によってファイルが縮小された場合は結果のファイルのサイズが使用され、ファイルが長くなった場合は元のファイルのサイズが使用されます)。また、-B オプションの後に数値を指定することでカスタマイズできます(例:75%を使用するように指示するには「-B75」)。

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つのファイルが「十分に類似しているか」を判断するために使用されます。デフォルトの50%とは異なる類似度スコアを使用するように、-M または -C オプションの後に数値を指定することでカスタマイズできます(例:「-M8」で8/10 = 80%を使用するように指示)。

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

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

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

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

分割されたファイルペアを元に戻す目的で、diffcore-break および diffcore-rename が使用する「変更の程度」の計算とは異なる計算を使用します。これは元の内容からの削除のみを数え、挿入は数えません。100行のドキュメントから10行だけを削除し、新しい1000行のドキュメントを作成するために910行の新しい行を追加したとしても、それは完全な書き換えではありません。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