Table of Contents
この記事を読んだ、またはGitのオブジェクトモデルを理解していることを前提に、Gitの git rebase
というコマンドについて説明する。
このコマンドは、コミット履歴を改変できるGit特有のコマンドで、分かり辛いGitコマンドの中でも最も分かり辛い部類のものだ。 Gitの最後の関門と言えよう。 けど、それだけに使いこなせばとても便利なものでもある。
git rebaseがもつたった一つの機能
git rebase
にはいろんなオプションがあって、ちょっと調べただけだと、コミットを移動する機能とコミットを修正する機能の二つがあるように見えるかもしれないが、実際は単一の機能しかないシンプルなコマンドだ。
その機能とは、指定した範囲のコミットが含む変更を、別に指定したコミットのコードベースに適用するというもの。
コマンドの基本形は次のようなものだ。
$ git rebase --onto master dev bugfix
このコマンドは、bugfix
から辿れるコミット群から、dev
から辿れるコミット群を除いたコミット群が含む変更を、master
のコードベースに適用する。
と書いても分からないので図解する。
このスライドを見ると、git rebase
に指定した3つのブランチのそれぞれの使われ方が分かるはず。
git rebase --onto master dev bugfix
が実行する処理をもっと正確に言うと、
bugfix
をcheckout
して(i.e.HEAD
をbugfix
にして)、dev..HEAD
のコミット群が含む変更を、それぞれ仮領域にパッチとして保存して、git reset --hard master
して、- 仮領域に保存した変更を、
HEAD
が指すコミットのコードベースにひとつひとつ順番に適用する。
上記コマンドでbugfix
のところを省略すると、ステップ1のcheckout
が省略される。
言い換えると、上記コマンドは次の二つのコマンドに分解できる。
$ git checkout bugfix
$ git rebase --onto master dev
さらに、--onto master
を省略すると、ステップ3のreset
先が変わり、dev
になる。
このときのコマンドの形は、
$ git rebase dev
という見慣れたものになるが、これが最初に挙げた基本形の省略形だと認識しておくと応用が利く。
以下にgit rebase dev
の動きを細かめに図解する。
インタラクティブモード
前節のスライドに書いたパッチの適用をカスタマイズできるのがインタラクティブモードで、これは-i
オプションで有効にできる。
インタラクティブモードを使うと、パッチをスキップしたり、順番を変えたり、まとめたり、分割したり、編集したりでき、またパッチとパッチの間に任意のコマンドを実行でき、例えばパッチごとにユニットテストを実行できたりする。
インタラクティブモードの使い方についてはググればたくさん出てくるのでここには書かない。 この記事辺りがわかりやすい。
インタラクティブモードのユースケースとしてよく紹介されるのが、git rebase -i HEAD^^
で直近の二つのコミットを変更するといったものだが、これを図解すると以下のようになる。
このスライドを見ると、git rebase dev
とgit rebase -i HEAD^^
は、パッチの適用がインタラクティブかどうか以外は同じ処理をしていることがわかる。
見た目の違いに惑わされないようにしたい。
git rebase
はブランチを複数指定したりして分かり辛いコマンドであることは確かだけど、上記の基本形を押さえておけばすんなり理解できるはず。