はじめに
業務でジュニアエンジニアにrebaseを教える機会がありました。 発端は、mainブランチから派生したfeatureブランチで作業中に、別のブランチがmainにマージされたことです。
彼はその差分を取り込むために、featureブランチへmainをマージしていました。 この方法も間違いではありませんが、コミット履歴が複雑になり、レビュー時に「どの変更が今回の作業か」を把握しづらくなります。 そのため、rebaseを使う方法を紹介しました。 良い機会なので、rebaseとは何か、mergeとの違いはどこにあるかといった観点でまとめます。
rebase とは
rebaseとは、文字通りブランチの「起点(ベース)」を付け替える操作です。 git rebase <ブランチ名> を実行すると、現在のブランチのコミットを指定したブランチの先頭に順番に再適用します。
コミット履歴が一直線になるため、後から変更の流れを追いやすくなります。
merge との違い
git merge と git rebase はどちらも別ブランチの変更を取り込む手段ですが、コミット履歴の形が異なります。
| 操作 | 履歴の形 | マージコミット |
|---|---|---|
git merge | 非線形(グラフ状) | 作成される |
git rebase | 線形(一直線) | 作成されない |
merge の場合
# feature ブランチで main の変更を取り込む
git checkout feature
git merge main
mergeを使うと、2つのブランチを統合するマージコミットが作成されます。 コミットグラフは分岐・合流を繰り返す形になります。
rebase の場合
# feature ブランチで main の変更を取り込む
git checkout feature
git rebase main
rebaseを使うと、featureブランチのコミットがmainの最新コミットの直後に再配置されます。 マージコミットが生まれないため、履歴が一直線になります。
以下のビフォー・アフターで動きを確認できます。
# rebase 前
main: A - B - C
\
feature: D - E
# rebase 後(feature のベースが C に移動)
main: A - B - C
\
feature: D' - E'
D・Eは内容が同じでも、ベースが変わるため新しいコミット(D’・E’)として再作成されます。
rebase を使うべきケース
今回のように featureブランチへmainの変更を取り込む場面はrebaseが適しています。 mergeコミットが増えず、レビュー時に「どの変更が今回の作業か」を把握しやすくなります。
一方で、すでにリモートにpush済みのブランチをrebaseした場合は注意が必要です。 コミットが再作成されるためコミットIDが変わり、他のメンバーの作業と競合する可能性があります。 push済みブランチへのrebaseは、自分だけが使っているブランチに限定することをおすすめします。
コンフリクトが発生した場合
rebase中にコンフリクトが発生した場合は、以下の手順で対応します。
ステップ1: コンフリクトを修正する
# コンフリクトしたファイルを確認する
git status
# ファイルを編集してコンフリクトを解消する
# (エディタで <<<< ==== >>>> のマーカーを取り除く)
# 注意:コンフリクトの解消には内容の理解が必要なので、どの変更を残すべきかを慎重に判断してください。
ステップ2: ステージングして続行する
# 修正したファイルをステージに追加する
git add <ファイル名>
# rebase を続行する
git rebase --continue
中断したい場合は git rebase --abort で元の状態に戻せます。
まとめ
rebaseはブランチの起点を付け替えることで、コミット履歴を一直線に保つ操作です。 featureブランチへmainの変更を取り込む場面ではmergeよりrebaseを使うことで、履歴が読みやすくなります。 push済みブランチへの適用には注意が必要ですが、ローカルで作業中のブランチであればぜひ積極的に活用してみてください。


コメント