git diff で変更を比較する

diff は 2 つの入力データセットを取得してそれらの間の変更を出力する機能です。git diff はさまざまな用途に使える Git コマンドで、実行すると Git データソースに対して diff 機能を実行します。対象のデータソースにはコミット、ブランチ、ファイルなどがあります。このドキュメントでは git diff の一般的な呼び出しと diff のワークフローパターンを取り上げます。git diff コマンドは Git リポジトリの現在の状態を分析するために git status および git log とともに使用されることがよくあります。

diff の見方: 出力

生の出力フォーマット

以下のサンプルはシンプルなリポジトリで実行されるものです。リポジトリは以下のコマンドで作成します。

$:> mkdir diff_test_repo
$:> cd diff_test_repo
$:> touch diff_test.txt
$:> echo "this is a git diff test example" > diff_test.txt
$:> git init .
Initialized empty Git repository in /Users/kev/code/test/.git/
$:> git add diff_test.txt
$:> git commit -am"add diff test file"
[master (root-commit) 6f77fc3] add diff test file
1 file changed, 1 insertion(+)
create mode 100644 diff_test.txt

この時点で git diff を実行しても何も出力されません。リポジトリには diff になるような変更がないため、これは想定内の挙動です。リポジトリを作成したら、diff_test.txt ファイルを追加してそのファイルのコンテンツを変更し、diff の出力を実際に見てみます。

$:> echo "this is a diff example" > diff_test.txt

このコマンドを実行すると diff_test.txt ファイルのコンテンツに変更が加えられます。変更が完了したら、diff を表示して出力を分析します。ここで git diff を実行すると以下の出力が生成されます。

diff --git a/diff_test.txt b/diff_test.txt
index 6b0c6cf..b37e70a 100644
--- a/diff_test.txt
+++ b/diff_test.txt
@@ -1 +1 @@
-this is a git diff test example
+this is a diff example

では diff の出力をさらに詳しく見ていきましょう。

1. 比較の入力

diff --git a/diff_test.txt b/diff_test.txt

この行には diff の入力元が表示されています。a/diff_test.txtb/diff_test.txt が diff に渡されているのがわかります。

2. メタデータ

index 6b0c6cf..b37e70a 100644

この行には内部 Git メタデータの一部が表示されています。この情報が必要になることはほぼないでしょう。この出力の数字は Git オブジェクトバージョンのハッシュ識別子に対応しています。

3. 変更箇所のマーカー

--- a/diff_test.txt
+++ b/diff_test.txt

これらの行は各 diff 入力ソースに記号を割り当てる凡例です。今回のケースでは a/diff_test.txt の変更が --- で示され、b/diff_test.txt の変更が +++ 記号で示されています。

4. diff チャンク

diff 出力のその他の部分は diff の「チャンク」の一覧です。diff ではファイルで変更されたセクションだけが表示されます。現在のサンプルでは単純なシナリオで作業しているため、チャンクは 1 つしかありません。チャンクには独自のきめ細かな出力セマンティックがあります。

@@ -1 +1 @@
-this is a git diff test example
+this is a diff example

最初の行はチャンクヘッダーです。各チャンクには @@ 記号で囲まれたヘッダーが先頭にあります。ヘッダーの中身はファイルに加えられた変更の概要になっています。今回のシンプルなサンプルには「-1 +1」がありますが、これは 1 行目に変更があることを意味しています。より現実に近い diff では、以下のようなヘッダーが表示されます。

@@ -34,6 +34,8 @@

このヘッダーのサンプルでは、34 行目から 6 行分が抽出されています。さらに、34 行目から 8 行分が追加されています。

diff チャンクの残りの部分には最新の変更が表示されます。変更された各行の先頭には、変更がどのバージョンの diff 入力のものかを示す + または - 記号が先頭に付きます。前に説明したように、-a/diff_test.txt の変更、+ は b/diff_test.txt の変更を示しています。

変更の強調表示

1. git diff --color-words

git diff にはよりきめ細かいレベルで変更を強調表示できる特別なモード ‐‐color-words があります。このモードでは追加された行と削除された行を空白でトークン化してそれらの差分を出します。

$:> git diff --color-words
diff --git a/diff_test.txt b/diff_test.txt
index 6b0c6cf..b37e70a 100644
--- a/diff_test.txt
+++ b/diff_test.txt
@@ -1 +1 @@
this is agit difftest example

これで、変更された単語に色付けされたものだけが出力に表示されるようになりました。

2. git diff-highlight

Git ソースをクローンすると、contrib というサブディレクトリに気づくでしょう。このディレクトリには Git 関連のツールや、git の基本機能としてまだ採用されていないこまごまとした面白いものが大量に入っています。たとえば、diff-highlight という Perl スクリプトがあります。diff-highlight は diff 出力で一致する行をグループ化して、変更された、通常の単語よりも小さい塊を強調表示します。

$:> git diff | /your/local/path/to/git-core/contrib/diff-highlight/diff-highlight
diff --git a/diff_test.txt b/diff_test.txt
index 6b0c6cf..b37e70a 100644
--- a/diff_test.txt
+++ b/diff_test.txt
@@ -1 +1 @@
-this is a git diff test example
+this is a diff example

これで、最小限の変更だけが diff として表示されるようになりました。

バイナリファイルの差分を取る

これまで実際に説明してきたテキストファイルユーティリティだけでなく、git diff をバイナリファイルに対して実行することもできます。ただし、デフォルトの出力はあまり役に立ちません。

$:> git diff
Binary files a/script.pdf and b/script.pdf differ

Git には、diff を実行する前にシェルコマンドを指定してバイナリファイルの中身をテキストに変換できる機能があります。ただし、これを行うにはちょっとした設定が必要です。まず、特定の種類のバイナリファイルをテキストに変換する方法を記入した textconv フィルターを指定する必要があります。今回は pdftohtml (homebrew から利用可能) というシンプルなユーティリティを使って PDF を人間が読める形の HTML に変換します。1 つのリポジトリでこれを設定するには、.git/config ファイルを編集するか、全体に実行する場合は ~ /.gitconfig を編集します。

[diff "pdfconv"]
textconv=pdftohtml -stdout

この後は 1 つ以上のファイルパターンを pdfconv フィルターと関連付けるだけです。これを行うには .gitattributes ファイルをリポジトリのルートに作成します。

*.pdf diff=pdfconv

設定が完了したら、git diff はまず設定済みのコンバータースクリプトでバイナリファイルを実行してコンバーター出力の差分を出します。zip や jar、その他のアーカイブ形式を含むすべての種類のバイナリファイルで同じ手法を使って有用な差分を出すことができます。pdf2html の代わりに unzip -l (または類似のコマンド) を使ってコミットイメージ間で追加または削除されたパスを表示します。exiv2 を使ってイメージディメンションドキュメントなどのメタデータの変更を表示できます。.odf や .doc などその他のドキュメント形式をプレーンテキストに変換する変換ツールもあります。いざという時には、正式なコンバーターがなくてもほとんどの場合、ストリングを使ってバイナリファイルを変換できます。

ファイルの比較: git diff ファイル

git diff コマンドには明示的なファイルパスオプションを渡すことができます。ファイルパスを git diff に渡すと、指定したファイルだけに diff 操作を実行できます。以下のサンプルでこの方法を説明します。

git diff HEAD ./path/to/file

このサンプルでは呼び出し時に対象を ./path/to/file に限定していて、作業ディレクトリとインデックスの特定の変更を比較することでまだステージされていない変更を表示しています。デフォルトでは、git diffHEAD に対して比較を実行します。上のサンプルの git diff ./path/to/file から HEAD を省略しても同じ結果が得られます。

git diff --cached ./path/to/file

--cached オプションを指定して git diff を呼び出すと、ステージ済みの変更とローカルリポジトリを比較して差分を出します。--cached オプションは --staged と同義です。

すべての変更を比較する

ファイルパスを指定せずに git diff を呼び出すと、リポジトリ全体の変更を比較します。上のファイル固有のサンプルは ./path/to/file 引数なしで呼び出すことができ、ローカルリポジトリのすべてのファイルで同じ出力結果が出ます。

最後のコミット以降の変更

デフォルトでは、git diff は最後のコミット以降コミットされていない変更を表示します。

git diff

2 つの異なるコミットを比較する

git diff に Git ref を渡して diff にコミットできます。ref の例としては、HEAD、タグ、ブランチ名があります。Git のすべてのコミットにはコミット ID があり、GIT LOG を実行して取得できます。このコミット ID を git diff に渡すこともできます。

git log --prety=oneline
957fbc92b123030c389bf8b4b874522bdf2db72c add feature
ce489262a1ee34340440e55a0b99ea6918e19e7a rename some classes
6b539f280d8b0ec4874671bae9c6bed80b788006 refactor some code for feature
646e7863348a427e1ed9163a9a96fa759112f102 add some copy to body
$:> git diff 957fbc92b123030c389bf8b4b874522bdf2db72c ce489262a1ee34340440e55a0b99ea6918e19e7a

ブランチを比較する

2 つのブランチを比較する

ブランチは git diff への他のすべての ref 入力のように比較されます。

git diff branch1..other-feature-branch

このサンプルではドット演算子を紹介します。このサンプルの 2 つのドットは、diff 入力が両方のブランチの先端であることを示しています。ドットを省略した場合も同じ結果が得られ、ブランチ間にスペースが使用されます。またドットが 3 つある演算子もあります。

git diff branch1...other-feature-branch

ドットが 3 つある演算子は、最初の入力パラメーター branch1 を変更して diff を開始します。これによって、2 つの diff 入力、branch1 の共有されている祖先、other-feature-branch の間の、共有されている共通の祖先のコミットの ref に branch1 を変更します。最後の入力パラメーターは other-feature-branch の先端のまま変わりません。

2 つのブランチからファイルを比較する

ブランチをまたいで特定のファイルを比較するには、ファイルのパスを 3 番目の引数として git diff に渡します。

git diff master new_branch ./diff_test.txt

概要

このページでは Git のdiff プロセスと git diff コマンドを取り上げました。git diff 出力の見方と、出力に含まれるさまざまなデータについて取り上げました。強調表示と色で git diff 出力を変更する様子を説明するサンプルを示しました。ブランチや特定のコミットのファイルで差分を取る方法など、さまざまな diff の手法を紹介しました。git diff コマンド以外にも、git loggit checkout を使いました。

Git を学ぶ準備はできていますか?

この対話式のチュートリアルをお試しください。

今すぐ始める