git 2-dot diffと3-dot diffの違い
はじめに
git diff
でブランチ間の差分を見る方法は3つある。
$ git diff branchA branchB # 空白で区切る
$ git diff branchA..branchB # ドット2つ
$ git diff branchA...branchB # ドット3つ
空白と2-dotは同じ意味なので気にする必要はないが、2-dotと3-dotは異なる結果を出すので注意が必要。特にGUIツールやWeb上のツールで違いを見たときそれがどぢらのdiffかを分かっていないとハマることがある。実際にハマった人を何人か見たことがあるのでここに違いを書いておく。
テスト環境
以下のようなmain
とfeature
というブランチがあるリポジトリを用意した。バージョンAのとき4行だったtest.txt
に対して、feature
ブランチでXX
を追加した。一方main
ブランチではYY
が追加されている。ここで実験してみる。
空白、2-dot、3-dotのdiffをそれぞれ実行すると以下のようになり、空白と2-dotは同じだが3-dotは異なることが分かる。
2-dotと3-dotの違い
2-dot
2-dotは単に指定されたブランチの違いを示している。言い換えればgit diff main..feature
は「main
(バージョンB) からfeature
(バージョンC) を作るにはどうするか」という質問の答えになっている。答えは表示のとおり「YYを消してXXを追加する」だ。「ブランチ間のdiff」と言ったとき想定するのはこちらの方だろう。
3-dot
3-dotの方の結果は+XX
しか出てこない。3-dotが何をしているかについては公式のヘルプを参照してみる(日本語訳はこのページから引用させてもらった)。
この説明に出てくるmerge-baseとは2つのブランチの共通の祖先のことで、上の例のmain
とfeature
のmerge-baseはバージョンAとなる。よってgit diff main...feature
はgit diff A feature
と同義ということになる。feature
は今バージョンCを指しているのでgit diff A C
になるということだ。確かにAからCへの変更はXXの追加なので、ヘルプの説明どおりの動作になっている。
3-dotとはつまり何なのか
これで3-dotがやっていることは分かったが、結局これは何を意味しているのか。実はそれほど難しい話ではなく、git diff main...feature
は「feature
をmain
にマージするとmain
に何が起こるか」という質問の答えになっている。結果は「XXを追加する」である。
順序を入れ替えるとどうなるか
2-dot diffで順序を入れ替えてgit diff feature..main
とすると、「feature
からmain
を作るにはどうするか」になり、+と-が入れ替わっている以外は同じ答えになる。diffの詳しいアルゴリズムは知らないので、常に同じものになるかは分からないが。
$ git diff feature..main
@@ -1,5 +1,5 @@
AA
+YY
BB
CC
DD
-XX
一方3-dotでgit diff feature...main
にすると、「main
をfeature
にマージすると何が起こるか」となり、以下のようになる。3-dot diffでは順番によって結果が大きく異なることに注意しよう。
$ git diff feature...main
@@ -1,4 +1,5 @@
AA
+YY
BB
CC
DD
Bitbucketでの比較
Bitbucketにはブランチの比較を表示する機能があるが、そこで使われているのは3-dotである。公式の説明にもその記載がある。ツールによって違いがあるだろうからマニュアルなどを参照しよう。
違いまとめ
-
git diff main..feature
:main
からfeature
を作るにはどうするか -
git diff feature..main
:feature
からmain
を作るにはどうするか - 空白区切りは2-dotと同じ
-
git diff main...feature
:feature
をmain
にマージするとmain
に何が起こるか -
git diff feature...main
:main
をfeature
にマージするとfeature
に何が起こるか
Discussion