Dev Study
← Gitの内部構造から理解する コース

10. rebase — なぜ履歴が「書き換わる」のか

「rebaseすると履歴が変わる」「共有ブランチでrebaseするな」とよく言われます。commitオブジェクトの構造を知っていれば、この理由は自分で導けます。

① rebaseは「親を付け替えたコミットを作り直す」

feature を main の先端に載せ替える
元: B ← E(feature)Eの parent は B
Eの変更内容を、Dの上に適用し直す
新: D ← E'(feature)E' の parent は D
  • ・parent が変われば、commitオブジェクトのバイト列が変わる
  • ・バイト列が変われば、ハッシュも必ず変わる → E と E' は別物
  • ・変更内容(差分)は同じでも、コミットとしては別のオブジェクト

コミットハッシュは parent を含めて計算されていました。親を付け替えた時点で、同じハッシュを保つことは原理的に不可能です。

② 以降のコミットが芋づる式に作り直される

1つ書き換えると、その先すべてが変わる
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つの具体例で確認します。

公式ドキュメントで詳しく ↗