Gitのリベースの説明
はじめに
Gitのrebaseは、(特にSubversionから入ってきた人にとって)理解が難しいものです。rebaseの説明はネットに多数落ちており(例えばPro Git)、わかってから読み返すと「なるほど」と思うのですが、理解があやふやな時にrebaseでトラブルが起きるとどうして良いかわからなくなりがちです。
優れた解説が多い中、さらなる記事を書くのは屋上屋を架す感がありますが、「僕はこうやって教えてほしかった」的な覚書を残しておきます。
コミットと差分
Gitのコミットは親コミットを覚えており、それをたどることで歴史を逆にさかのぼって行くことができます。Gitでは歴史を玉と線で表現することが多いです。玉はコミットを表し、コミットはその時点でのスナップショットを表しています。玉と玉の間の線は差分(パッチ)を表しており、一つ前のコミットにそのパッチを適用することで次のコミットが得られる、と解釈できます。
マージとリベース
いま、歴史が分岐しているとしましょう。master
とbranch
の二つのブランチがあり、共通のコミットからそれぞれ歴史が進んでいます。branch
で加わった修正をmaster
に取り込みたいとき、Gitはmerge
かrebase
の二つの手段を選ぶことができます。
マージの場合
マージする場合は両方の修正を一度に取り込んだコミットを作ります。master
ブランチからbranch
にたいしてgit merge
をかけた場合、こんな感じになります。
この時、二つの歴史が一つになります。したがって、現在master
が指しているコミットの親は二つになります。親とつながる線は「その親から自分になるための修正パッチ」を意味しているため、それぞれ図示するとこんな感じになります。
リベースの場合
branch
ブランチの修正をマージでmaster
に取り込みたいとき、master
からbranch
に対してgit merge
をかけました。それに対して、リベースで取り込みたいときには、まずbranch
でmaster
に対してgit rebase
をします。
するとGitは、master
とbranch
の共通祖先から、branch
の現在のコミットまでを切り出し、master
の先につなげます。これによりbranch
の指すコミットの直接の祖先がmaster
になるため、Fast Forwardマージが可能になります。
この図だけ見ると、branch
にぶら下がっていたコミットを「移動」したように見えますが、実際にはbranch
のコミット間から「パッチ」を取り出し、それを順番に適用することで新たにコミットを作っています。先ほどまでの図の例で見てみましょう。
この図を見ると、新しくできたc1'
やc2'
コミットは、リベース前の対応するコミットc1
、c2
とは異なるスナップショットを表していることがわかります。むしろ変わっていないのは、c1
やc1'
から親コミットに向かって伸びる線が表すパッチです。つまり、「リベースとは、玉(コミット)ではなく、線(パッチ)を移動する操作である」と理解できます。
リベースのsquash
リベースが「共通祖先からリベース元にいたるまでのパッチを、リベース先に次々と適用することだ」と理解できると、rebase -i
で出てくるsquash
の意味もわかります。
先ほどの状態でbranch
からrebase -i master
を実行すると、どのコミットをどうするかを聞かれます。今回のケースでは二つコミットがあるので、それぞれについて対応を聞かれます。rebase -i
で選べる対応はpick
、reword
、edit
、squash
、fixup
、x
がありますが、よく使うのはpick
(コミットを使う)と、squash
(コミットを使うが一つ前のコミットと融合してしまう)でしょう。
デフォルトはpick
です。-i
をつけずにrebase
した場合は、リベース対象となっている玉の数だけ、リベース先にくっつくことになります。
squash
は、コミットをまとめます。図を見ると「玉(コミット)をまとめる」というよりは「線(パッチ)をまとめる」といった方が実態に近い気がします。
まとめ
Gitのリベースが何をやっているかを説明してみました。「Gitの歴史の表示においてコミットがスナップショット、コミット間の線がパッチを表す」ということ、「リベースは、共通祖先からリベース元へのパッチを、リベース先に次々適用することで一本の歴史を作る作業である」ということを理解するのに時間がかかりました。これがわかってしまうと、例えばマージならコンフリクトが一度しかおきないのに、リベースでは何度もコンフリクトが起きることがある理由がわかったり、squashにより空のパッチができてしまってリベースに失敗して焦るようなこともなくなるでしょう。
本稿が「リベースわからん」同志の助けになれば幸いです。
Discussion