はじめに
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 switch master
% git merge --ff feature
※--ff
オプションは省略可能。
○ = Commit
◎ = HEAD
○ -- ○ -- ○ (master)
\
○ -- ○ -- ◎ (feature)
このように、masterブランチとfeatureブランチが持つ過去のコミットが完全に一致していると自動的にfast-forward mergeとなります。
fast-forward mergeはHEAD位置の変更だけを行うため、マージコミットは発生しません。そのため、後からfeatureブランチの内容が見つけづらくなったり、featureブランチのコミットを取り消すのが煩雑になるなどのデメリットがあります。
Gitのデフォルト設定はfast-forward mergeです。
non-fast-forward
% git switch master
% git merge --no-ff feature
○ = Commit
◎ = HEAD
○ -- ○ -- ○ --------------- ◎ (master)
\ /
○ -- ○ -- ○ (feature)
このように、masterブランチとfeatureブランチが持つ過去のコミットが完全に一致していても、--no-ff
オプションをつけることでnon-fast-forward mergeとなります。
non-fast-forward mergeはマージコミットが発生します。そのため、後からfeatureブランチの内容が見つけやすく、featureブランチのコミットを取り消すのも簡単になります。
Gitのデフォルト設定はnon-fast-forward mergeではないため、--no-ff
オプションなしでnon-fast-forward mergeを行いたい場合、設定を変更する必要があります。
マージの設定
git config
マージの種類をnon-fast-forward mergeにしたい場合、以下のコマンドを実行します。
% 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
で取り消しコミットを残す必要があるので注意してください。
まとめ
必ずしもfast-forward mergeが悪いというわけではないと思いますが、fast-forward mergeはマージコミットが発生しないため、社内ルールとして「マージはnon-fast-forwardで行う」と決められていることがあります。
Gitの理解を深めるためにも、本記事を参考にしてfast-forwardとnon-fast-forwardについて勉強していただければと思います。