git diffはワークツリーと何を比較する?ステージング?それともコミット?

Gitを勉強中にgit diff(オプション無し)の使い方について疑問に思ったことをまとめます。

それはgit diffは何と何を比較するのか?ということです。

いくつか参考サイトを見たところ次のように書いてありました。

git diff は何と何を比較するのか

自分が調べた範囲だとgitの解説サイトにはgit diffが何と何を比較するのかについて、次の二つのパターンがありました。

サイトA

ワークツリーとステージングを比較

サイトB

ワークツリーと最新のコミットを比較

はたしてどちらが正しいのでしょうか?

結論から言うとステージングに同じファイルがあるかどうかでgit diffの比較対象は変わるので、どちらもありえます。

git diff の動作の違い

ワークツリーとステージングに同じファイルがある場合とない場合で、git diffの動作は次のように変わります。

ステージングに同じファイル git diffの動作
ある ワークツリーとステージングを比較
ない ワークツリーと最新のコミットを比較

今の内容を図で示してみます。 *1

a)ステージングに同じファイルがある場合

flowchart LR
1[(ワークツリー)] <-- git diff --> 2[(ステージング)] 
2[(ステージング)] <-- git diff staged --> 3[(最新コミット)]

1[(ワークツリー)] <-- git diff HEAD --> 3[(最新コミット)]

b) ステージングに同じファイルがない場合

flowchart LR
1[(ワークツリー)] <-- git diff <br> git diff HEAD --> 3[(最新コミット)]

git diffgit diff HEADと同じ対象を比較しています。

今後の使い分け

考え方としてはgit diffの比較元は必ずワークツリーであり、比較対象はステージングに同じファイルがあればステージング、ステージングに同じファイルがなければ最新のコミットとなります。

git diff --stagedは比較元をステージングに変更するオプションだと考えれば覚えやすいと思います。

またワークツリーと最新コミットを比較するコマンドはgit diff HEADですが、ステージングに同じファイルがない場合にはgit diffでも同様の動作をします。そのためgit diffの動作を把握せずにgit diffコマンドを使用していると、git diffの比較対象がステージングなのか最新コミットなのか分からなくなってしまう可能性があります。

そのため自分は今後次のように使い分けることで、比較対象を明らかにしようと思います。

やりたいこと コマンド
ワークツリーとステージングを比較したい git diff
ワークツリーと最新のコミットを比較したい git diff HEAD

参考資料

gitの公式マニュアル*2のdiffの説明(Examples)には次のようにあります。

Various ways to check your working tree

$ git diff (1)

$ git diff --cached (2)

$ git diff HEAD (3)

  1. Changes in the working tree not yet staged for the next commit.

  2. Changes between the index and your last commit; what you would be committing if you run git commit without -a option.

  3. Changes in the working tree since your last commit; what you would be committing if you run git commit -a

git diffはワーキングツリーの変更を表示するとしか書かれていないので、 比較対象はステージングの場合もあるし、最新コミットの場合もあるということで良いのかなと思います。

今回の記事について違うよというご指摘があれば是非お願いします。

※今回使用したGitのバージョンは2.34.1です。

*1:Mermaid記法で図を作成したのですが、--を単語の中で使う方法がわからなかったので、diff --stagedがdiff stagedという表記になってしまっています。

*2:https://www.git-scm.com/docs/git-diff