Linux & IT ノート

Git で履歴を整える:rebase の基本とインタラクティブ rebase

管理人 約10分で読めます

前回はブランチを作ってマージする基本的な流れを学びました。 今回は git rebase を取り上げます。merge とは別のアプローチでブランチを統合しつつ、コミット履歴を直線的に保つ強力な機能です。

rebase とは何か

rebase は、ブランチの「分岐元」を別のコミットに付け替える操作です。 文字通り「ベース(base)を付け替え(re)する」という意味です。

merge との違いを理解するために、まず両者の挙動を比較してみましょう。

【共通の出発点】
main:    A → B → C
feature:       → D → E

merge の場合:mainとfeatureの両方の変更を取り込むマージコミット M が作られます。

main:    A → B → C → M

feature:   → D → E

rebase の場合:feature ブランチの分岐元を C に付け替え、コミットが C の上に積み直されます。

main:    A → B → C → D' → E'

rebase 後の D' E' は元のコミット D E とハッシュが異なります。同じ変更内容を持つ新しいコミットとして作り直されているからです。結果として、マージコミットのない一直線の履歴が得られます。

git rebase main の流れ

フィーチャーブランチを最新の main の上に載せ替えるのが、rebase の最もよくある使い方です。

# feature ブランチで作業中
git switch feature/add-login

# main の最新を取得してから rebase
git fetch origin
git rebase main

git rebase main を実行すると、Git は次の手順を自動で行います。

  1. featuremain の共通の祖先(分岐点)を探す
  2. feature にのみあるコミットを一時的に退避する
  3. feature のベースを main の先端に移す
  4. 退避したコミットを順番に再適用する

リモートリポジトリから最新を取り込む git pull にも --rebase オプションがあります。

git pull --rebase origin main

こちらは git fetch + git rebase を一度に行います。git pull(fetch + merge)の代わりにこれを使う習慣をつけると、無駄なマージコミットが減り履歴がすっきりします。

merge vs rebase の使い分け

どちらが「正しい」というものではなく、状況によって使い分けます。

merge を選ぶとき

  • ブランチの合流という事実を履歴に残したい
  • チームでの PR マージなど、「このブランチを取り込んだ」という記録が重要な場合

rebase を選ぶとき

  • 手元の作業ブランチを最新の main に追随させたい
  • プッシュ前にコミット履歴を整理してレビューしやすくしたい
  • マージコミットのない直線的な履歴を保ちたいチームポリシーの場合

ただし、後述するrebase の黄金律を先に頭に入れておくことが大切です。

インタラクティブ rebase でコミットを整理する

git rebase -i(インタラクティブ rebase)は、過去のコミット群を対話的に編集できる機能です。 「細かくコミットしたけれどプッシュ前にまとめたい」「コミットメッセージを直したい」という場面で重宝します。

直近3件のコミットを対象にする場合は HEAD~3 を指定します。

git rebase -i HEAD~3

エディタが起動し、次のような画面が表示されます。

pick a1b2c3d Add login form
pick b4c5d6e Fix typo in login
pick c7d8e9f Add password validation

各行の先頭の pick を別のキーワードに変えることで操作を指定します。

キーワード省略形動作
pickpそのまま使用(デフォルト)
squashs直前のコミットに統合(メッセージを結合)
fixupf直前のコミットに統合(メッセージは捨てる)
rewordrコミットメッセージを書き直す
dropdそのコミットを削除する
edite一時停止してコミット内容を修正する

例えば「Addログインフォーム」と「Fixタイポ」を1つにまとめたい場合は次のようにします。

pick a1b2c3d Add login form
fixup b4c5d6e Fix typo in login
pick c7d8e9f Add password validation

ファイルを保存してエディタを閉じると、指定した操作が順番に実行されます。

rebase 中のコンフリクトと対処法

rebase はコミットを一つずつ再適用するため、コンフリクトが起きた場合はその時点で処理が止まります。

git rebase main
# CONFLICT (content): Merge conflict in auth.py
# error: could not apply b4c5d6e... Fix typo in login
# hint: Resolve all conflicts manually, ...

対処の流れは次のとおりです。

# 1. コンフリクトしたファイルを確認する
git status

# 2. エディタでコンフリクトマーカーを解消する(次回詳解)
#    ファイルを編集してマーカーを取り除く

# 3. 解消したファイルをステージングする
git add auth.py

# 4. rebase を続ける
git rebase --continue

コミットが複数ある場合は、次のコミットを再適用する際にまた同様の処理が必要になることがあります。 一方、「やっぱり rebase を中止したい」という場合は次のコマンドで元の状態に戻せます。

git rebase --abort

--abort を使うと、rebase 開始前の状態にきれいに戻ります。

rebase の黄金律:push 済みブランチは rebase しない

rebase で最も重要なルールが**「他の人と共有したブランチは rebase しない」**です。

なぜかを理解するため、次のシナリオを考えます。

あなたが feature ブランチをリモートにプッシュし、同僚がそのブランチを git pull して作業を始めたとします。 ここであなたが feature ブランチを rebase してリモートへ強制プッシュ(git push --force)すると、同僚が手元に持っているコミット履歴と、リモートの履歴が完全に食い違います

rebase はコミットを「作り直す」操作なので、同じ変更でもハッシュが変わります。 その結果、同僚が次に git pull しようとすると大量のコンフリクトや重複コミットが生じ、リポジトリの履歴が壊れた状態になります。

安全な使い方の境界線はシンプルです。

  • 手元にしかない(まだ push していない)ブランチ → rebase OK
  • リモートに push 済み、あるいは他のメンバーが使っているブランチ → rebase しない

自分だけの作業ブランチであっても、リモートに push した後は事情が変わります。 やむを得ず rebase して --force-with-lease で上書きする場合は、必ずチームに事前に共有してください。

実践的なコツ

プル時に rebase を使う

git pull --rebase

これを常用すると、git pull で生まれがちな「マージコミット」を防げます。 グローバルに設定することもできます。

git config --global pull.rebase true

インタラクティブ rebase の前に作業ブランチをバックアップする

git rebase -i を使い慣れていない間は、rebase 前にブランチのバックアップを作っておくと安心です。

git branch backup/feature-login feature/add-login

万一 rebase が意図通りにいかなくてもバックアップブランチから復旧できます。

git log --oneline --graph で履歴の形を確認する

git log --oneline --graph --all

rebase の前後でどのようにグラフの形が変わったか視覚的に確認できます。 --all をつけるとすべてのブランチが表示されるので全体像が把握しやすいです。

まとめ

  • rebase はブランチの分岐元を付け替えて、コミット履歴を直線的に整える操作
  • git rebase main でフィーチャーブランチを最新の main の上に乗せ直せる
  • merge がマージコミットを作るのに対し、rebase はマージコミットを作らない
  • git rebase -i HEAD~N でコミットのまとめ・メッセージ修正・削除ができる
  • rebase 中のコンフリクトは git rebase --continue、中止は git rebase --abort
  • push 済みの共有ブランチを rebase してはいけない(黄金律)
  • git pull --rebase を使うと日常的なプルのマージコミットを減らせる

次回はコンフリクト(マージ競合)を取り上げます。 コンフリクトマーカーの読み方と、焦らず手順通りに解消する方法を詳しく解説します。