GitHubでのチーム開発において、Pull Request(PR)は欠かせない機能です。PRを通じてコードレビューを行い、品質を担保しながら開発を進めることができます。PRのマージ方法には「Create a merge commit(マージコミットの作成)」「Squash merge(スカッシュマージ)」「Rebase merge(リベースマージ)」の3種類があります。これらの違いを理解し、プロジェクトに合った方法を選択することが重要です。
1. 3種類のマージ方法の違い
Create a merge commit(マージコミットの作成)
これはGitHubのデフォルトのマージ方法です。featureブランチの全てのコミット履歴を保持したまま、マージを行います。マージの際に新たな「マージコミット」が作成され、2つのブランチが合流したことを示します。
特徴:
- featureブランチの全コミット履歴が保持される
- マージの証拠となる専用のマージコミットが作成される
- 開発の流れが完全に記録される
コミットの状態:
A---B---C---D---F (main)
\ /
E1--E2--E3 (feature)
マージ後:
A---B---C---D---F---G (main) ← Gはマージコミット
\ /
E1--E2--E3 (feature)
- E1、E2、E3の全てのコミットが履歴に残る
- マージコミットGが作成される
- ブランチの分岐と合流が明確に表示される
Squash merge(スカッシュマージ)
featureブランチの全てのコミットを1つのコミットにまとめて(スカッシュして)、mainブランチにマージします。PRの全ての変更は単一のコミットとしてmainブランチに追加されます。
特徴:
- 複数のコミットが1つにまとめられる
- mainブランチのコミット履歴がシンプルになる
- PRのタイトルと説明がコミットメッセージとして使用される
- 個々の作業ステップの詳細は失われる
コミットの状態:
A---B---C---D---F (main)
\
E1--E2--E3 (feature)
マージ後:
A---B---C---D---F---E' (main) ← E'はE1, E2, E3をまとめたコミット
- E1、E2、E3が統合されて単一のコミットE'になる
- ブランチの分岐点は履歴に残らない
- コミット履歴がシンプルになる
Rebase merge(リベースマージ)
featureブランチのコミットを個別にmainブランチに適用します。マージコミットは作成されず、コミット履歴は線形になります。
特徴:
- featureブランチの各コミットが個別にmainブランチに追加される
- マージコミットは作成されない
- コミット履歴が線形になる
- 各コミットのSHAハッシュが変更される(新しいコミットとして扱われる)
コミットの状態:
A---B---C---D---F (main)
\
E1--E2--E3 (feature)
マージ後:
A---B---C---D---F---E1'---E2'---E3' (main) ← E1', E2', E3'は再適用されたコミット
- E1、E2、E3が個別にE1'、E2'、E3'として再適用される
- コミットの内容は同じだが、新しいSHAハッシュが割り当てられる
- 線形の履歴になる
マージ方法の比較表
特性 | Create a merge commit | Squash merge | Rebase merge |
---|---|---|---|
コミット履歴 | 全てのコミットが保持される | 1つのコミットに統合される | 個別のコミットとして保持されるが再作成される |
マージコミット | 作成される | 作成されない | 作成されない |
履歴の形状 | 分岐と合流が見える | 線形 | 線形 |
コミットのSHA | 元のままで保持 | 新しいコミット1つのみ作成 | 全てのコミットが新しいSHAで再作成 |
履歴の詳細さ | 最も詳細 | 最も簡潔 | 詳細だが線形 |
トレーサビリティ | 完全に保持される | PRレベルでのみ保持 | コミットの内容は保持されるが、元のSHAは変更 |
用途の例 | 開発過程の詳細な記録 | 機能単位での整理されたコミット | 読みやすい線形履歴の維持 |
2. 実際の開発フローでの使い分け
GitHub上の開発では、「Git Flow」と呼ばれる開発フローがよく採用されています。Git Flowは、長期開発ブランチ(mainやdevelop)と短期トピックブランチ(feature、release、hotfix)を使い分けることで、安定した開発を実現します。このフローにおける各ブランチのマージ種別について見ていきましょう。
Git Flowの基本的なブランチ構成
Git Flowでは、主に以下のブランチを使用します:
- main: 本番環境のコード(リリース済み)
- develop: 開発中のコード(次回リリース用)
- feature: 新機能開発用の短期ブランチ
- release: リリース準備用のブランチ
- hotfix: 緊急バグ修正用のブランチ
feature ブランチから develop ブランチへのマージ
推奨マージ方法: Squash merge
develop: A---B---C---D---F---E' (E'はfeatureブランチの変更をまとめたコミット)
\
feature: E1--E2--E3
理由:
- 開発者が試行錯誤したコミット履歴を整理でき、developブランチをクリーンに保てる
- 一つの機能実装を一つのコミットとして記録できる
- PRのタイトルと説明をコミットメッセージとして活用できる
- コードレビューの結果を反映した最終的な変更だけを残せる
設定方法:
# developブランチでSquash mergeを推奨する設定例(リポジトリ設定)
ブランチ保護ルール > develop > マージ方法の制限 > 「Allow squash merging」にのみチェック
release ブランチから main ブランチへのマージ
推奨マージ方法: Create a merge commit
main: M1------M2------M3------M4------------------M5
\ /
develop: D1--D2--D3--D4--D5--D6--D7--D8--D9--D10
\ /
release: R1--R2--R3--R4--R5--R6
理由:
- リリースの事実を明示的に記録できる(マージコミットがリリースポイントになる)
- リリースの範囲が履歴から明確に分かる
- リリースタグを付けやすい(マージコミットにタグを付ける)
- リリース単位での変更追跡が容易になる
設定例:
# mainブランチでCreate a merge commitを推奨する設定例
ブランチ保護ルール > main > マージ方法の制限 > 「Allow merge commits」にのみチェック
release ブランチから develop ブランチへのマージ
推奨マージ方法: Create a merge commit または Rebase merge
# Create a merge commitの場合
develop: D1--D2--D3--D4--D5--D6--D7--D8--D9--D10----D11
\ /
release: R1--R2--R3--R4--R5--R6
# Rebase mergeの場合
develop: D1--D2--D3--D4--D5--D6--D7--D8--D9--D10--R3'--R4'--R5'--R6'
\
release: R1--R2--R3--R4--R5--R6
理由:
- リリース準備中に行った修正をdevelopブランチにも反映する必要がある
- Create a merge commitの場合:リリース準備での修正が一連の変更として分かりやすい
- Rebase mergeの場合:修正を個別のコミットとして反映させつつ、線形の履歴を維持できる
hotfix ブランチから main ブランチへのマージ
推奨マージ方法: Create a merge commit
main: M1------M2------M3------M4---------------M5------M6
\ / /
hotfix: H1--H2
理由:
- 緊急修正の事実を明示的に記録できる
- 修正内容が明確に分かる
- 修正パッチとしてのバージョン管理がしやすい
hotfix ブランチから develop ブランチへのマージ
推奨マージ方法: Squash merge または Rebase merge
# Squash mergeの場合
develop: D1--D2--D3--D4--D5--D6--D7--D8--D9--D10--D11--H'
(H'はhotfixの変更をまとめたコミット)
# Rebase mergeの場合
develop: D1--D2--D3--D4--D5--D6--D7--D8--D9--D10--D11--H1'--H2'
理由:
- 本番環境で行った緊急修正を開発環境にも反映する必要がある
- Squash mergeの場合:「本番の緊急修正の取り込み」として一つのコミットにまとめられる
- Rebase mergeの場合:修正内容を詳細に残しつつ、線形の履歴を維持できる
実際のワークフローにおける使い分けのポイント
-
チームの規模と経験に応じた使い分け
- 小規模チーム・経験の浅いチーム:Squash mergeを中心に使うと履歴が分かりやすい
- 大規模チーム・経験豊富なチーム:Create a merge commitで詳細な履歴を残す
-
コミット粒度の管理
- コミット粒度が適切に保たれている場合:Rebase mergeやCreate a merge commitが適している
- コミット粒度が細かすぎる場合:Squash mergeで整理する
-
ブランチの役割に応じた使い分け
- 長期ブランチ同士のマージ(release→main):Create a merge commit
- 短期ブランチから長期ブランチへのマージ(feature→develop):Squash merge
- バグ修正や変更の伝播(hotfix→develop):用途に応じてSquashかRebase
-
自動化とCI/CDの考慮
- デプロイが特定のコミットに紐づいている場合:Create a merge commitで明示的なマージポイントを作る
- テスト自動化がコミット単位で走る場合:Squash mergeで無駄なテスト実行を減らす
チーム開発での考慮点
プロジェクトやチームに合ったマージ戦略を選ぶ際には、以下の点を考慮すると良いでしょう:
-
リポジトリ単位でデフォルト設定を決める
- プロジェクトの性質に合わせて、リポジトリのデフォルトマージ方法を設定する
- チーム全体で一貫性を持たせる
-
ブランチ保護ルールの設定
- 重要なブランチ(mainやdevelopなど)に対して、特定のマージ方法のみを許可する
- 意図しないマージ方法の使用を防止する
-
コミットメッセージの規約を設ける
- 特にSquash mergeを使用する場合、PRのタイトルとなるコミットメッセージの形式を統一する
- 例:
[機能名] 実装内容の簡潔な説明
-
プロジェクトのドキュメントに明記する
- 採用するマージ戦略をREADME.mdやCONTRIBUTING.mdに明記する
- 新メンバーがチームの慣習を理解しやすくする
まとめ
GitHubのPull Requestにおけるマージ方法は、プロジェクトの性質やチームの好みによって選択すべきものです。履歴の詳細さを重視するなら「Create a merge commit」、メインブランチのコミット履歴をシンプルに保ちたいなら「Squash merge」、線形の履歴が好ましいなら「Rebase merge」が適しています。
どの方法が「最良」というわけではなく、プロジェクトごとに最適な方法を選択することが重要です。チーム内で方針を統一し、一貫性のあるマージ戦略を採用することで、効率的な開発が可能になります。