git mergeの種類!--ff・--no-ff・--squashの違いとは?

Gitでコードを管理していると、異なるブランチを統合する「マージ」が必要になります。その際、git mergeコマンドを使用しますが、git mergeにはいくつかのオプションがあり、オプションによって挙動が変わります。

このブログでは、以下の4つの主要オプションについて、「使い方」や「メリット/デメリット」をわかりやすく解説します。

  • --ff
  • --ff-only
  • --no-ff
  • --squash

git mergeのオプションの種類

git mergeには以下に示すオプションがあり、それぞれが異なる動作をします。

  • --ff
  • --ff-only
  • --no-ff
  • --squash

これらのオプションはどのように違うのでしょうか?

これから各オプションについて、「特徴」や「使い方」、「メリット/デメリット」を解説します。

--ff(デフォルト動作)

git mergeのオプション(--ff)

--ffは「Fast-Forward」の略で、デフォルトで適用されるオプションです。

マージ先のブランチ(上図の例だとdevelopブランチ)が変更されておらず、単純に先に進めるだけでマージが可能な場合、Fast-Forwardマージ(早送りマージ)を行います。マージ先のブランチが変更されている場合には、後ほど説明する3wayマージ(Non-Fast-Forwardマージ)が行われます。

例えば、featureブランチの作業をdevelopブランチに統合したい場合、以下を実行します。

git checkout develop
git merge --ff feature # git merge feature と同じ

上図に示しているのは、featureブランチに行った変更(コミットAとコミットB)をdevelopブランチにマージした場合の図です。左側の図の場合、developブランチは変更されていないので、git merge --ff featureを実行すると、Fast-Forwardマージ(早送りマージ)が行われます。一方、右側の図の場合、developブランチに変更(コミットX)が入っているので、git merge --ff featureを実行すると、3wayマージ(Non-Fast-Forwardマージ)が行われます。

特徴

  • 新しいマージコミットを作成せず、ブランチ履歴をそのまま進める。
  • マージ先のブランチが変更されている場合には、この後に説明する3wayマージ(Non-Fast-Forwardマージ)が行われる。

メリット

  • 履歴がクリーンで見やすい。
  • 不要なマージコミットを避けられる。

デメリット

  • ブランチ統合のタイミングが履歴から分かりにくい。

--ff-only

git mergeのオプション(--ff-only)

--ff-onlyは、Fast-Forwardマージ(早送りマージ)のみを許可するオプションです。Fast-Forwardマージが可能でない場合、マージは失敗します。

例えば、featureブランチの作業をdevelopブランチに統合したい場合、以下を実行します。

git checkout develop
git merge --ff-only feature

上図に示しているのは、featureブランチに行った変更(コミットAとコミットB)をdevelopブランチにマージした場合の図です。左側の図の場合、developブランチは変更されていないので、git merge --ff-only featureを実行すると、Fast-Forwardマージ(早送りマージ)が行われます。一方、右側の図の場合、developブランチに変更(コミットX)が入っているので、git merge --ff-only featureを実行してもマージをすることができません。

特徴

  • Fast-Forwardが可能な場合のみマージが実行される。
  • 意図しない3wayマージを防げる。
  • マージ先のブランチが変更されている場合には、マージをすることができません。

メリット

  • --ffで説明した内容と同じなので省略

デメリット

  • --ffで説明した内容と同じなので省略

--no-ff

git mergeのオプション(--no-ff)

--no-ffは、Fast-Forwardが可能な場合でも、新しいマージコミットを必ず作成する3wayマージ(Non-Fast-Forwardマージ)を行うオプションです。

例えば、featureブランチの作業をdevelopブランチに統合したい場合、以下を実行します。

git checkout develop
git merge --no-ff feature

上図に示しているのは、featureブランチに行った変更(コミットAとコミットB)をdevelopブランチにマージした場合の図です。developブランチに変更有無にかかわらず、git merge --no-ff featureを実行すると、必ず3wayマージ(Non-Fast-Forwardマージ)が行われます。

特徴

  • 必ず新しいマージコミットを作成する。

メリット

  • 履歴にブランチ統合のタイミングが明確に記録される。
  • 大規模プロジェクトや複雑なブランチ管理に適している。

デメリット

  • 履歴が複雑になり、読みづらくなる場合がある。

--squash

git mergeのオプション(--squash)

--squashは、マージ元ブランチの全てのコミットを1つにまとめて、1回のコミットとして適用するオプションです。

例えば、featureブランチの作業をdevelopブランチに統合したい場合、以下を実行します。

git checkout develop
git merge --squash feature
git commit -m "Squashed feature branch" # マージコミットが作られないため、--squashの場合は自分でコミットする必要があります。

上図に示しているのは、featureブランチに行った変更(コミットAとコミットB)をdevelopブランチにマージした場合の図です。developブランチに変更有無にかかわらず、git merge --squash featureを実行すると、featureブランチに行った変更(コミットAとコミットB)を1つにまとめられます。ただし、マージコミットは作成されないので、自分でコミットする必要があります。

特徴

  • マージ元のブランチの履歴は無視され、1つのコミットにまとめられます。
  • マージコミットは作成されない。

メリット

  • 履歴が簡潔になり、特定の機能を1つのコミットにまとめられる。
    • コミットが1つにまとめられるため、「新機能の変更はこのコミット」というのがすぐに分かる。
  • レビューやリリース時に便利。
    • 1つのコミットにまとまっていることで、レビュー時に変更点を簡単に確認でき、リリースノートも明確になるため。

デメリット

  • 元のコミット履歴が失われるため、詳細な変更履歴を追いづらい。
  • チームでの作業時に注意が必要。
    • チームメンバーが「feature ブランチで何をしたか」を理解しようとする場合、詳細な履歴がないため、コミット1つでは意図や経緯を把握できないことがある。

git mergeの各オプションの使い分け

git mergeのどのオプションを使用するかは、以下のように使い分けるとよいと思います。

  • 履歴をシンプルにしたい
    • --ffまたは--ff-onlyを使う
    • 余計なマージコミットを作らず、履歴がクリーンになるため
  • 履歴を明確に残したい
    • --no-ffを使う
    • ブランチの統合タイミングが履歴から分かるため
  • 変更を1つにまとめたい
    • --squashを使う
    • 複数のコミットを1つにまとめ、リリースやレビューが簡単になるため

本記事のまとめ

この記事では『git mergeコマンドのオプション』について、以下の内容を説明しました。

  • --ff
  • --ff-only
  • --no-ff
  • --squash
  • git mergeの各オプションの使い分け

お読み頂きありがとうございました。