Git の履歴を書き換える | git rebase

git rebase コマンド

rebase は、ブランチの起点となるコミットを別のコミットへ移動する操作である。一般的な動作はチュートリアル図のようになる。

Git チュートリアル: rebase でプロジェクト履歴の線形性を保つ

見た目には、rebase はブランチをあるコミットから別のコミットへ移動する手段にすぎないように見える。しかし Git の内部では、新しいコミットを作成し、それを対象のベースコミットへ適用することで実現している。つまり、これはそのままプロジェクト履歴を書き換える操作である。ここでは、ブランチ自体は同じように見えても、それを構成するコミットはまったく別物であることを理解しておく必要がある。

使い方

git rebase <base>

現在のブランチを <base> の上へ rebase するコマンドである。rebase 対象には、コミット ID、ブランチ、タグ、HEAD からの相対参照など、あらゆる種類のコミット参照を使用できる。

補足説明

rebase の主な目的は、プロジェクト履歴の線形性を保つことである。たとえば、機能の作業を開始した後に master ブランチが進んだシナリオを考える。

機能を master ブランチへ統合するには、直接マージする方法と、rebase してからマージする方法の 2 つがある。最初の選択肢では three-way マージとマージコミットが必要になる。一方、後者では fast-forward マージが可能になり、履歴の線形性を完全に保てる。チュートリアル図は、rebase 後のバージョンが fast-forward マージ可能になる理由を説明している。

Git チュートリアル: fast-forward マージ

rebase は、上流側の変更をローカルリポジトリへ統合する一般的な方法である。git merge を使って上流側の変更を取り込むと、プロジェクトの進行状況を確認するたびに不要なマージコミットが作成されることがある。これに対して rebase は、「自分の変更作業はすべて、完了済みの最新変更を基準にしたい」という操作に相当する。

公開済みコミットの rebase は厳禁

git commit --amendgit reset の説明と同じく、公開リポジトリに push されたコミットを rebase してはならない。rebase は過去のコミットを新しいコミットに置き換えるため、プロジェクト履歴の一部が欠落したように見える。

使用例

次の例は、プロジェクト履歴の線形性を保つために git rebasegit merge を併用するものである。これは fast-forward マージを手早く確実に行うための簡単な方法である。

# Start a new feature
git checkout -b new-feature master
# Edit files
git commit -a -m "Start developing a feature"

機能開発中にコードベースのセキュリティ脆弱性が見つかったとする。

# Create a hotfix branch based off of master
git checkout -b hotfix master
# Edit files
git commit -a -m "Fix security hole"
# Merge back into master
git checkout master
git merge hotfix
git branch -d hotfix

hotfix を master にマージすると、プロジェクト履歴には分岐が発生する。そこで単に git merge を使うのではなく、この機能ブランチを rebase して、履歴の線形性を保ちながら統合できるようにする。

git checkout new-feature
git rebase master

これにより new-featuremaster の先端へ移動するため、master と通常の fast-forward マージが可能になる。

git checkout master
git merge new-feature