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

名前

gitdiffcore - diff出力の調整

概要

git diff *

説明

diffコマンドである`git diff-index`、`git diff-files`、`git diff-tree`は、`diff`出力を表示する前に、見つかった差異を従来とは異なる方法で操作するように指示できます。この操作は、まとめて「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エントリはリストから削除されます。

比較の結果は、これらのコマンドから内部的に「diffcore」と呼ばれるものへ渡され、-pオプションを使用しない場合の出力と同様の形式になります。例えば

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は、修正前後のファイルの内容(上記の例では、「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行だけ削除した場合でも、新しい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を2回実行してgrepを実行するため、非常にコストがかかる可能性があります。速度を上げるために、textconvフィルタのないバイナリファイルは無視されます。

-Sまたは-G--pickaxe-allなしで使用する場合、それぞれの基準に一致するファイルペアのみが出力に保持されます。--pickaxe-allを使用する場合、変更セット内で1つのファイルペアでもそれぞれの基準に一致すれば、変更セット全体が保持されます。この動作は、変更セット全体のコンテキストで変更を確認しやすくすることを目的としています。

diffcore-order: ファイル名に基づいて出力をソートするため

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

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

例として、コアGitの典型的なorderfileは次のようになります。

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