【Git】マージにおけるfast-forwardとnon-fast-forwardの違い

はじめに

Gitにおけるマージには、fast-forwardとnon-fast-forwardの2種類があります。これらの違いを理解することは、Gitの使い方を習得する上で重要です。本記事では、fast-forwardとnon-fast-forwardマージの特徴と使い分けについて詳しく解説します。

マージの種類

現在のブランチ構造

以下のように、masterブランチからfeatureブランチが派生しており、featureブランチをmasterブランチにマージしようとしている状況を想定します。

○ = Commit
◎ = HEAD

○ -- ○ -- ◎                 (master)
            \
              ○ -- ○ -- ○   (feature)

なお、masterブランチからfeatureブランチが派生してからmasterブランチに新たなコミットはないものとします。以下のようにfeatureブランチが派生してからmasterブランチに新たなコミットが発生している場合、featureブランチをマージすると自動的にnon-fast-forward mergeとなります。

○ = Commit
◎ = HEAD

○ -- ○ -- ○ ------ ◎        (master)
            \
              ○ -- ○ -- ○   (feature)

fast-forward

git merge --ff featureコマンドを使うと、fast-forwardマージが行われます。この場合、masterブランチとfeatureブランチの過去のコミットが完全に一致しているため、HEADの位置を単にfeatureブランチに移動させるだけで済みます。

% git switch master
% git merge --ff feature

--ffオプションは省略可能。

○ = Commit
◎ = HEAD

○ -- ○ -- ○                 (master)
            \
              ○ -- ○ -- ◎   (feature)

fast-forwardマージは、マージコミットを作成しないため、ブランチの履歴が線形に保たれます。ただし、featureブランチの内容を後から追跡するのが難しくなる可能性があります。

non-fast-forward

git merge --no-ff featureコマンドを使うと、non-fast-forwardマージが行われます。この場合、masterブランチとfeatureブランチの過去のコミットが完全に一致していても、新しいマージコミットが作成されます。

% git switch master
% git merge --no-ff feature
○ = Commit
◎ = HEAD

○ -- ○ -- ○ --------------- ◎   (master)
            \             /
              ○ -- ○ -- ○       (feature)

non-fast-forwardマージは、マージコミットを作成するため、ブランチの履歴が明確に残ります。featureブランチの内容を後から追跡しやすくなり、ブランチの取り消しも容易になります。

使い分けのポイント

fast-forwardマージとnon-fast-forwardマージの使い分けは、プロジェクトの方針や開発スタイルによって異なります。一般的には、ブランチの履歴を明確に残したい場合はnon-fast-forwardマージを、ブランチの履歴が煩雑にならないようにしたい場合はfast-forwardマージを選択するのが良いでしょう。

  • 小規模な変更や短期的な開発ブランチの場合
    • fast-forwardマージを使用
    • 履歴がシンプルに保たれ、不要なマージコミットを避けられる
  • 長期的な開発ブランチや重要な機能開発の場合
    • non-fast-forwardマージ(--no-ffオプション)を使用
    • ブランチの存在と統合の履歴が明確に残り、後から変更を追跡しやすい
  • プロジェクトの方針として一貫性を保つ場合
    • すべてのマージをnon-fast-forwardに統一
    • プロジェクト全体の履歴が一貫して管理され、各ブランチの統合ポイントが明確になる
  • リリースブランチやメインブランチへのマージ
    • non-fast-forwardマージを推奨
    • 重要な統合ポイントを明確に記録し、必要に応じて容易にロールバックできる
  • チーム開発やオープンソースプロジェクトの場合
    • non-fast-forwardマージを推奨
    • 複数の開発者の貢献を明確に追跡でき、プロジェクトの進行状況が把握しやすい
  • CIプロセスとの統合
    • プロジェクトのCI/CDパイプラインの要件に応じて選択
    • コミットハッシュに基づいてビルドやデプロイを行う場合は、fast-forwardが適している場合がある
  • コードレビューのワークフロー
    • プルリクエスト/マージリクエストを使用する場合は、non-fast-forwardを推奨
    • レビュー履歴とマージの関係が明確になる
  • ブランチ戦略に応じた使い分け
    • Git Flowを採用している場合
      • develop、release、hotfixブランチへのマージはnon-fast-forwardを推奨
    • GitHub Flowを採用している場合
      • mainブランチへのマージはnon-fast-forwardを推奨

マージの設定

git config

Gitの設定を変更することで、non-fast-forwardマージをデフォルトの動作にすることができます。

% git config --global --add merge.ff false
% git config --global --add pull.ff only

なお、一行目の設定だけではpullしたときのマージもnon-fast-forward mergeになり、pullするたびにマージコミットが発生してしまうため、pullしたときはfast-forward mergeとする設定も併せて行います。

Sourcetree

すべてのマージ設定

[Sourcetree] - [環境設定] - [Git] - [マージ時にfast-forwardせずに...] を有効にします。

個別のマージ設定

[fast-forward可能でも...] を有効にします。

VSCode

拡張機能「Git Graph」の設定です。

[Create a new commit even...] を有効にします。

トラブルシューティング

fast-forward mergeをしてしまったが、non-fast-forward mergeに変更したい場合、fast-forward mergeを取り消した後、改めてnon-fast-forward mergeを行います。

# fast-forward mergeの取り消し
% git reset --hard HEAD^
# or
% git revert HEAD^

# 改めてnon-fast-forward merge
% git switch master
% git merge --no-ff feature

コミットをリモートにpushしている場合、revertで取り消しコミットを残す必要があるので注意してください。

まとめ

Gitのマージ方式には、fast-forwardとnon-fast-forwardの2種類があります。それぞれの特徴を理解し、プロジェクトの要件に合わせて適切な方式を選択することが重要です。本記事を参考に、Gitのマージ方式について理解を深めていただければ幸いです。

関連記事

【Git】コマンドスニペット(随時更新)
# git pull `git pull`コマンドは、リモートリポジトリから最新の変更を取得し、現在のローカルブランチに統合するための重要な操作です。`--rebase`オプションを付けるかどうかで統合方法が異なります。 ## 概要 [...]
2024年7月29日 14:10