← Gitの内部構造から理解する コース
10. rebase — なぜ履歴が「書き換わる」のか
「rebaseすると履歴が変わる」「共有ブランチでrebaseするな」とよく言われます。commitオブジェクトの構造を知っていれば、この理由は自分で導けます。
① rebaseは「親を付け替えたコミットを作り直す」
元: B ← E(feature)Eの parent は B
Eの変更内容を、Dの上に適用し直す
新: D ← E'(feature)E' の parent は D
- ・parent が変われば、commitオブジェクトのバイト列が変わる
- ・バイト列が変われば、ハッシュも必ず変わる → E と E' は別物
- ・変更内容(差分)は同じでも、コミットとしては別のオブジェクト
コミットハッシュは parent を含めて計算されていました。親を付け替えた時点で、同じハッシュを保つことは原理的に不可能です。
② 以降のコミットが芋づる式に作り直される
E → E'ハッシュが変わった
Fの parent は E だったので、Fも作り直しになる
F → F'ハッシュが変わる
同様に、その先すべて
G → G' …以降全部
- ・古い E・F・G はオブジェクトDBに残っているが、どのブランチからも参照されなくなる
- ・他の人が古いハッシュを持っていると、履歴が食い違う
- ・これが「共有ブランチでrebaseするな」と言われる理由
第2部で見たハッシュチェーンの性質そのものです。過去を1つ変えると、それ以降の全ハッシュが変わります。改ざん検出の仕組みが、そのまま「履歴書き換えの代償」として現れます。
③ mergeとrebaseの使い分け
merge既存コミットは不変。合流点が残る
rebaseコミットを作り直す。履歴は一本になる
- ・merge: 履歴が事実に忠実。分岐と合流がそのまま見える
- ・rebase: 履歴が読みやすい直線になる。ただしハッシュは別物になる
- ・共有済み(pushした)コミットをrebaseすると他人の履歴と衝突する
- ・一般的な運用は「自分だけのローカルブランチはrebase可、共有ブランチはmerge」
どちらが優れているかではなく、「既存のコミットを変えないか、作り直すか」という性質の違いです。チームの方針に従ってください。
サンプルコードで身につける(5本) →
学んだ概念を、素朴な例から実務寄りの例まで5つの具体例で確認します。
公式ドキュメントで詳しく ↗