GitHubのプルリクエストでは、以下3つのマージ方法が選べます。
これらはどのように違うのでしょう?
実際にやってみて違いを見てみたいと思います。
Create a merge commit
: 通常のマージ
通常のマージは簡単に言うと
- マージコミットを作ってマージする(マージの履歴が残る)
です。実際にやってみましょう。
test-1
ブランチにはtest-1
とtest-2
というコミットが積まれている状態です。
余談ですが、このGitのログは
git log --graph --all --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative
というコマンドで表示しています。
長いのでgitコマンドのエイリアスに登録することをおすすめします。
ではこのtest-1
ブランチをmain
ブランチにCreate a merge commit
でマージします。
すると、水色枠で示したようなマージコミットが作成されます。
マージコミットの特徴として、親が2つある事が挙げられます。
つまり、上図でいうと緑枠で示した、マージ元の最後のコミット(test-2
)と、マージ先のmain
ブランチの最後のコミット(Initial commit
)です。
こうすることで、どのマージでどのコミットをマージしたかが後々も簡単にわかります。
もっと言うと、Git自身もこれまでのマージでどのコミットをマージしたかを辿ることができます。
これが通常マージの特徴です。
Squash and merge
: スカッシュしてマージ
スカッシュしてマージは簡単に言うと
- コミットを一つにまとめて
- マージコミットを作らずにマージする(マージの履歴が残らない)
です。実際にやってみましょう。
squash
ブランチで以下のようなsquash 1
とsquash 2
というコミットをmain
にスカッシュしてマージします。
ここで、GitHub上の画面でコミットメッセージを作成します。今回はデフォルトのままSquash (#2)
でいきます。
(#2 はプルリクエストの番号です。スカッシュしてマージするときには、後でたどりやすくするため、コミットメッセージにプルリクエストの番号を入れることをおすすめします。)
すると以下のように水色のコミットが作成されます。
(見づらかったのでsquash
ブランチは削除しています)
シンプルですね。
squash 1
やsquash 2
というコミットは跡形もなく消えて、Squash (#2)
というコミットだけが、元のmain
ブランチに積まれています。
このSquash (#2)
というコミットにはsquash 1
とsquash 2
の両方の変更が含まれています。
マージコミットは作らないので、Git自身はどのマージでどのコミットがマージされたか辿れません。
ただし上記のようにコミットメッセージにプルリクエストの番号を入れることで、人間はどのプルリクエストでどのコミットがマージされたか辿ることはできます。
Rebase and merge
: リベースしてマージ
リベースしてマージは簡単に言うと
- マージコミットを作らずにマージする(マージの履歴が残らない)
です。
Squash (#2)
のコミットが最後であるmain
からrebase-and-merge
ブランチを作成し、下記のようにrebase 1
とrebase 2
というコミットを積みます。
これをリベースしてマージをした後が以下です。
(見やすくするためrebase-and-merge
ブランチはマージ後に削除しています)
ぱっと見ほとんど変わりませんね。
よく見ると、rebase-and-merge
ブランチでの変更が、そのままmain
ブランチに取り込まれていることがわかります。
このように、リベースしてマージは、マージ元(ここではrebase-and-merge
ブランチ)のコミットをそのままマージ先(ここではmain
ブランチ)のブランチ積み上げます。
スカッシュしてマージと同じく、マージコミットは作らないので、Git自身がどのマージでどのコミットがマージされたかは辿れません。
また、新たにコミットを作るわけでもないので、Gitの履歴からどのコミットがどのプルリクエストと関連づいているのか、人間が辿ることも難しいです。
どう使い分ける?
ここまでで、それぞれの違いがなんとなく理解できたかと思いますが、結局どう使い分けるの? という疑問が残りますね。
このあたりは運用の考え方で次第で様々に使い分けがされると思います。
簡単にメリット・デメリットをまとめると以下のようになると思います。
* PR: プルリクエスト
マージ方法 | メリット | デメリット |
---|---|---|
通常のマージ | どのPRでどのコミットがマージされたかわかりやすい。マージの履歴が残る。 | マージ元のコミットが残るので多少見づらい。 |
スカッシュしてマージ | コミットがPRごとに1つにまとまるので見やすい。新たにコミットを作るので、コミットメッセージにPR番号を入れればPRとの関連付けもできる。 | Git上でマージの履歴が残るわけではないので、過去にマージしたことのあるブランチに新たにコミットを積んで再度マージしようとすると、マージ済みのコミットも全てマージしようとするため、大抵マージコンフリクトが発生する。 |
リベースしてマージ | スカッシュしてマージとは違い、コミットが全て残る。 | 上記に加えて、PRとの関連付けができない。(どのPRでどのコミットがマージされたかがわからない) |
簡単にと言いましたが、抽象的でよくわかりませんね…
このあたりを深掘りすると長くなってしまいそうなので、こちらは別記事で書いていきたいと思います。
簡単に、使い分けの例として以下が挙げられると思います。
通常のマージ
- マージ元ブランチをマージ後も消さずに使い続ける場合
- 例えばGit Flowにおける
develop
ブランチをmain
ブランチにマージするときなど - これはマージ履歴が残ることに関係します
- 例えばGit Flowにおける
- ベースブランチ(
main
やdevelop
)へのマージ- 注意すればスカッシュしてマージでも対応可能
- 変更が巨大など、マージ履歴を残しつつマージ後もコミット履歴を残したい場合
スカッシュしてマージ
- マージ元ブランチはマージ後に消してしまう場合
-
feature
ブランチからさらに派生したブランチをfeature
ブランチにマージする場合 - Git Flowにおける
feature
ブランチをdevelop
にマージする場合- ただしマージする際に最新の
develop
にリベース済みである必要がある
- ただしマージする際に最新の
-
- コミットを1つにまとめても変更が巨大でない場合
リベースしてマージ
- コミット履歴をそのまま、マージ先ブランチに取り込みたい場合
-
feature
ブランチからさらに派生したブランチをfeature
ブランチにマージする場合
-
おわりに
以上、簡単にGitHubのプルリクエストのマージ方法を整理しました。
それぞれの特徴を理解して使い分ける必要がありそうです。
わからなければ、無難に通常マージをするのが良さそうです。