フィーチャーブランチで開発を進めていると、mainブランチとの差分が大きくなり、マージ時に複雑なコンフリクトが発生することがありますよね。また、試行錯誤の過程で作成した細かいコミットが履歴に残り、後から見返したときにわかりにくくなることもあります。
git rebaseを使えば、コミット履歴を整理して線形でクリーンな状態に保てます。この記事では、rebaseの基本から実践的な使い方、チーム開発での注意点まで解説します。
git rebaseの概要
git rebaseは、あるブランチのコミットを別のブランチの上に再適用するコマンドです。mergeとは異なり、マージコミットを作成せずにブランチを統合できます。
rebase実行時、Gitは以下の手順で処理を行います。まず両方のブランチの共通祖先を見つけ、現在のブランチの各コミットで導入された差分を抽出します。次にターゲットブランチの先端にHEADをリセットし、抽出した変更を順番に再適用します。
この処理により、元のブランチは新しいコミットに置き換えられます。コミットの内容は同じでも、コミットハッシュは変更される点に注意してください。
基本的な使い方
シンプルなrebase
featureブランチをmainブランチの最新状態に追従させる基本的な例です。
# featureブランチにいる状態で
git checkout feature
# mainブランチの変更を取り込む
git rebase mainrebase後、featureブランチのコミットはmainブランチの先端から始まる形になります。
ブランチを指定したrebase
現在のブランチ以外を対象にrebaseすることもできます。
# featureブランチをmainの上にrebase(現在どのブランチにいても実行可能)
git rebase main feature–ontoオプションで移植先を指定
特定のブランチの一部を別のベースに移植する際に使用します。
# 構文
git rebase --onto <新しいベース> <古いベース> <対象ブランチ>
# 例:topicブランチをnextからmasterに移植
git rebase --onto master next topicこの例では、topicブランチがnextから分岐した後のコミットだけを、masterブランチの上に移植します。
mergeとrebaseの使い分け
mergeとrebaseはどちらもブランチを統合するコマンドですが、結果が異なります。
| 観点 | merge | rebase |
|---|---|---|
| 履歴 | 分岐履歴を保持(マージコミット作成) | 線形履歴を作成 |
| コミット | 新しいマージコミットを追加 | コミットを再作成 |
| 元のコミット | 変更なし | 新しいハッシュで再作成 |
| 追跡性 | 並行開発の経緯がわかる | シンプルで読みやすい |
mergeはチーム協業でブランチの履歴を保持したい場合や、公開ブランチの統合時に適しています。監査目的でコード変更の完全な追跡性が必要なプロジェクトでも有効です。迷った場合はmergeを選ぶのが安全です。
一方rebaseは、ローカルで作業中のコミットを整理したい場合に適しています。プッシュ前のクリーンアップや、フィーチャーブランチをmainの最新状態に追従させる際に使用します。線形履歴によりgit bisectやgit logが使いやすくなります。
インタラクティブモード
インタラクティブモードは、rebaseの最も強力な機能の一つです。コミットの編集、統合、削除、順序変更などを対話的に行えます。
起動方法
# 直近5コミットを編集
git rebase -i HEAD~5
# 特定コミット以降を編集
git rebase -i abc1234利用可能なコマンド
| コマンド | 短縮形 | 説明 |
|---|---|---|
| pick | p | コミットをそのまま使用 |
| reword | r | コミットメッセージを編集 |
| edit | e | コミットで停止して編集可能に |
| squash | s | 前のコミットに統合(メッセージ編集可) |
| fixup | f | 前のコミットに統合(メッセージ破棄) |
| drop | d | コミットを削除 |
| exec | x | シェルコマンドを実行 |
squashとfixupの違い
squashは複数コミットを1つに統合し、すべてのコミットメッセージを編集できます。fixupも同様に統合しますが、統合されるコミットのメッセージは破棄され、統合先のメッセージのみが残ります。
実践例:コミットの整理
開発中に作成した細かいコミットを整理する例を見てみましょう。
# 編集画面(git rebase -i HEAD~4 を実行)
pick abc1234 Add login feature
squash def5678 Fix typo in login
squash ghi9012 Add validation
pick jkl3456 Add logout featureこの設定でrebaseを実行すると、最初の3つのコミットが1つに統合されます。squashを指定したコミットは、直前のpickまたはsquashコミットに統合されます。
コンフリクトの解決
rebase中にコンフリクトが発生した場合、Gitは処理を中断してユーザーに解決を求めます。
解決の流れ
# 1. コンフリクトしているファイルを編集して解決
# 2. 解決したファイルをステージング
git add <解決したファイル>
# 3. rebaseを続行
git rebase --continueその他のオプション
# 現在のコミットをスキップして次に進む
git rebase --skip
# rebaseを中止して元の状態に戻す
git rebase --abortrerereで解決を再利用
git rerere(reuse recorded resolution)を有効にすると、過去に解決したコンフリクトパターンを記録し、同じコンフリクトが発生した際に自動的に再適用できます。
git config --global rerere.enabled truerebaseを頻繁に行うワークフローでは、rerereを有効にしておくと作業効率が上がります。
ベストプラクティス
やるべきこと
ローカルでの整理に活用しましょう。プッシュ前にコミットを整理してクリーンな履歴を作成できます。squashやfixupで実験的なコミットをまとめることで、レビューしやすい履歴になります。
早めに頻繁にrebaseすることも重要です。ブランチの分岐期間が長くなるほどコンフリクトが増えます。定期的にmainの変更を取り込むことで、マージ時の問題を軽減できます。
rebase前にブランチのバックアップを作成しておくと安心です。git branch backup-feature featureのようにバックアップブランチを作成しておけば、問題が発生しても元の状態に戻せます。
避けるべきこと
公開済みコミットのrebaseは絶対に避けてください。他の開発者が参照しているコミットをrebaseすると、チーム全体に影響します。共有ブランチでは絶対にrebaseしないでください。
同様に、mainやmaster等の共有ブランチをrebaseすると、全員のリポジトリに不整合が発生します。
force pushの注意点
rebase後のプッシュには--forceが必要ですが、--force-with-leaseを使用する方が安全です。
# 推奨:他者の変更を上書きしない
git push --force-with-lease origin feature-branch
# 非推奨:他者の変更を上書きする危険あり
git push --force origin feature-branch--force-with-leaseは、ローカルが認識しているリモートの状態と実際のリモートが一致する場合のみプッシュを許可します。他の人がプッシュした変更を誤って上書きすることを防げます。
便利な設定
pull時にrebaseを使用
git config --global pull.rebase trueこの設定により、git pull実行時にmergeではなくrebaseが使用されます。ローカルのコミットがリモートの変更の上に再適用されるため、不要なマージコミットを避けられます。
autosquashを有効化
git config --global rebase.autoSquash trueautosquash機能を有効にすると、fixup!やsquash!で始まるコミットメッセージがインタラクティブrebase時に自動的に適切な位置に配置されます。
# 元のコミット
git commit -m "Add login feature"
# 修正コミット(後でsquash対象になる)
git commit -m "fixup! Add login feature"
# rebase実行時に自動的にfixupとして配置される
git rebase -i HEAD~2autoStashを有効化
git config --global rebase.autoStash trueこの設定により、作業中の変更がある状態でrebaseを実行すると、自動的にstashしてrebase後に復元されます。
よくある質問
rebaseとmergeはどちらを使うべきですか?
基本的な指針として、ローカルで作業中の未プッシュコミットにはrebaseを、公開済みのコミットや共有ブランチにはmergeを使用します。チームのワークフローに従うことが最も重要で、迷った場合はmergeを選ぶのが安全です。
rebase後にpushできないのはなぜですか?
rebaseはコミット履歴を書き換えるため、リモートブランチと履歴が diverge(分岐)した状態になります。git push --force-with-leaseを使用してプッシュしてください。ただし、共有ブランチでは絶対に行わないでください。
rebaseを取り消す方法はありますか?
git reflogを使用して、rebase前の状態を見つけ、git reset --hardで戻すことができます。
# reflogで履歴を確認
git reflog
# rebase前の状態に戻す
git reset --hard HEAD@{2}–preserve-mergesは使えますか?
--preserve-mergesオプションはGit 2.22で非推奨となり、代わりに--rebase-mergesが導入されました。マージコミットを含むブランチをrebaseする場合は--rebase-mergesを使用してください。
まとめ
git rebaseはコミット履歴を整理してクリーンに保つための強力なツールです。インタラクティブモードを活用すれば、コミットの統合、編集、順序変更が自由に行えます。
ただし、公開済みのコミットをrebaseすることは避け、ローカルでの作業整理に限定して使用することが重要です。チーム開発では、rebaseとmergeを適切に使い分けることで、効率的なワークフローを実現できます。
まずはgit rebase -i HEAD~3でインタラクティブモードを試してみましょう。コミット履歴を整理する感覚をつかめば、日々の開発がより快適になるはずです。
参考リンク
Git – git-rebase Documentation – 公式リファレンス
Git – Rebasing – Git公式ブックの解説
Merging vs. Rebasing | Atlassian Git Tutorial – mergeとrebaseの使い分けガイド
Git interactive rebase, squash, amend | thoughtbot – インタラクティブrebaseの詳細解説



