CH 25 / 75第4部 ブランチとマージ

rebase を理解する

このページで分かること

  • rebase が「コミットを別の土台の上に付け替える」操作だと説明できる
  • git rebase main で枝分かれを1本に並べ直せる
  • rebase するとコミットの ID が変わる理由が分かる
  • 共有したコミットを rebase してはいけない理由と、取り消し方が分かる
このページの目次9 items

23と24では、枝分かれした履歴をマージで1つに合わせました。このページでは、もう1つの取り込み方である rebase(リベース)を扱います。rebase は、ブランチのコミットを付け替えて、履歴を1本にまっすぐ並べ直す操作です。

rebase は「土台を付け替える」#

rebase の base は、ブランチが枝分かれした根元のコミット、つまり土台のことです。re-base は「土台を付け替える」という意味です。

練習用のリポジトリで、枝分かれした状態を作ります。ブランチでプリンのレシピを2回に分けて書き、その間に main でもコミットします。

text11 lines
cd ~/git-practice
git switch -c recipe-pudding
echo "卵 3個" > recipes/pudding.txt
git add recipes/pudding.txt
git commit -m "プリンのレシピを追加"
echo "牛乳 300ml" >> recipes/pudding.txt
git commit -am "プリンに牛乳を追加"
git switch main
echo "ゴミを出す" >> todo.txt
git commit -am "やることにゴミ出しを追加"
git log --oneline --decorate --graph --all -n 5
text6 lines
* 66549f1 (HEAD -> main) やることにゴミ出しを追加
| * ad7f9e8 (recipe-pudding) プリンに牛乳を追加
| * 5cf83e1 プリンのレシピを追加
|/  
*   52db35e Merge branch 'oat-milk'
|\  

recipe-pudding の土台は 52db35e です。そのあと main は 66549f1 に進みました。ここでマージすれば23と同じ 3-way マージになりますが、rebase ではプリンの2つのコミットを、main の最新 66549f1 の上に乗せ直します。

左は rebase の前で、コミット B から main 側のゴミ出しコミット X と、recipe-pudding 側の P1・P2 に枝分かれしている。右は git rebase main のあとで、B・X のあとに付け替えた P1′・P2′ が1本に並び、元の P1・P2 はどのブランチからも外れている図
rebase は、ブランチのコミットを新しい土台の上に付け替える

git rebase を実行する#

rebase は、付け替えたいブランチに立ってから、新しい土台を指定します。マージ(取り込まれる側に立つ)とは向きが逆なので注意してください。

text2 lines
git switch recipe-pudding
git rebase main
text1 lines
Successfully rebased and updated refs/heads/recipe-pudding.
text1 lines
git log --oneline --decorate --graph --all -n 4
text5 lines
* 7137480 (HEAD -> recipe-pudding) プリンに牛乳を追加
* e0c6b9b プリンのレシピを追加
* 66549f1 (main) やることにゴミ出しを追加
*   52db35e Merge branch 'oat-milk'
|\  

枝分かれが消え、プリンの2つのコミットが main の続きとして1本に並びました。ブランチを作ったのが「ゴミ出しのあと」だったかのような履歴です。

ここで main に取り込むと、23の Fast-forward になります。

text2 lines
git switch main
git merge recipe-pudding
text5 lines
Updating 66549f1..7137480
Fast-forward
 recipes/pudding.txt | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 recipes/pudding.txt

マージコミットを作らずに取り込めました。「rebase してから Fast-forward でマージする」のが、rebase のいちばん典型的な使い方です。

text1 lines
git branch -d recipe-pudding

なぜ ID が変わるのか#

rebase の前後で、同じ「プリンのレシピを追加」の ID が 5cf83e1 から e0c6b9b に変わっています。

第3部の17で見たとおり、コミットには親のコミットの ID が書き込まれていて、コミットの ID はその中身全体から計算されます。rebase で親が 52db35e から 66549f1 に変われば、中身が変わるので ID も変わります。つまり rebase は、元のコミットを動かしているのではなく、同じ変更を持つ新しいコミットを作り直しているのです。

途中経過は reflog で確かめられます(第2部の15)。

text1 lines
git reflog -n 5
text5 lines
7137480 HEAD@{0}: rebase (finish): returning to refs/heads/recipe-pudding
7137480 HEAD@{1}: rebase (pick): プリンに牛乳を追加
e0c6b9b HEAD@{2}: rebase (pick): プリンのレシピを追加
66549f1 HEAD@{3}: rebase (start): checkout main
ad7f9e8 HEAD@{4}: checkout: moving from main to recipe-pudding

共有したコミットは rebase しない#

ID が変わることから、rebase には大事な決まりが1つあります。

rebase してよいのは、まだ自分の手元にしかないコミットです。GitHub に送る(第6部の39の push)前のブランチなら、自由に rebase して構いません。

rebase の途中でコンフリクトしたら#

rebase はコミットを1つずつ作り直すので、その途中でコンフリクトすることがあります。止まったときの直し方は24と同じで、ファイルを直して git add までは共通です。そのあとが少し違います。

コマンド動き
git rebase --continue直したコミットを作り直し、残りのコミットへ進む(マージの git commit にあたる)
git rebase --skip今のコミットを捨てて、次へ進む
git rebase --abortrebase をやめて、始める前の状態に戻す

マージと rebase、どちらを使うか#

マージrebase
履歴の形枝分かれと合流がそのまま残る1本にまっすぐ並ぶ
既存のコミット変えない作り直す(ID が変わる)
共有後に使えるか使える使えない
コンフリクトの解決1回でまとめてコミットごとに起きることがある

どちらが正しいということはありません。「何が起きたかをそのまま残す」のがマージ、「読みやすい形に整えてから残す」のが rebase です。迷ったら、共有した履歴はマージ、自分の手元の作業ブランチを最新の main に追いつかせるときは rebase、と使い分けてください。チームでどちらに揃えるかは、28のブランチ戦略で扱います。

よくある疑問#

Q. git pull --rebase という書き方を見かけました。
A. GitHub などから最新の変更を取り込むときに、マージではなく rebase を使うオプションです。考え方はこのページと同じで、自分のまだ送っていないコミットを、取り込んだ最新の履歴の上に付け替えます。第6部の40で扱います。

Q. main のほうを rebase してもいいですか?
A. main は共有されていることがほとんどなので、避けてください。rebase するのは、自分の作業ブランチを main の上に付け替える方向だけ、と決めておくと安全です。

まとめ#

  • rebase は、ブランチのコミットを新しい土台の上に付け替えて、履歴を1本に並べ直す
  • 付け替えたいブランチに立って git rebase main。そのあと main で取り込むと Fast-forward になる
  • 付け替えたコミットは作り直された別のコミットで、ID が変わる。元のコミットは reflog からたどれる
  • 共有したコミットは rebase しない。rebase するのは自分の手元にしかないコミットだけ
  • 途中でコンフリクトしたら、直して git add → git rebase --continue。やめるなら --abort

次のページ#

26. interactive rebase で履歴を整える ── rebase の対話モードで、コミットをまとめる・消す・メッセージを直すなど、共有する前の履歴を整えます。