LoginSignup
5
5

git rebase の動作を --onto 指定も含めて正しく理解する

Last updated at Posted at 2022-10-14

git rebase の引数と動作解説

はじめに

リベースコマンド実行の大半は git rebase main で済みますが、それはrebaseコマンドの引数省略形に過ぎません。すこし複雑なリベースをしようとすると悩んでしまうのは、引数省略しない使い方を理解していないためです。

本記事は、https://git-scm.com/docs/git-rebase に記載されている説明を、厳密な正確性よりも分かりやすさを優先して書き換えたものです。
git reabse の使い方に悩む方々の参考になればと思い公開します。
これを読めば思い通りのリベースが出来るようになるでしょう。

コマンドライン指定形式

git rebase [--onto 接続先] [切り取り開始点 [切り取り終了点]]

切り取り開始点から切り取り終了点までの複数コミットを、接続先 へ付け替えるのがリベースの動作であり、私たちが日頃良く使うのは --onto 接続先切り取り終了点 の引数を省いた形なのです。

例えば git rebase maingit rebase --onto main main current-branch の省略形です。切り取り開始点がmainで、接続先もmainでは何も起きないのではないか。と思われるでしょうが、その疑問については 後の章 にて説明します。
先に引数を省くと何が補われるのかを説明しましょう。

引数省略時の説明

切り取り開始点(upstream)

省略時は切り取り終了点(branch)に設定されたリモートブランチ(上流ブランチ)の最新コミットを使う。

切り取り終了点(branch)

省略時はカレントブランチの最新コミットが対象となる。

接続先(newbase)

省略時は切り取り開始点(upstream)を使う。

動作

git rebase --onto 接続先 切り取り開始点 切り取り終了点

このgitコマンドは下記ステップで実行されます。

  1. 切り取り終了点 にスイッチし、それをカレントブランチとする。
    git switch 切り取り終了点
    • 切り取り終了点にブランチ名を指定していない場合は、そこを最終コミットとする仮ブランチとして扱う。
    • この時点で切り取り終了点HEADになる。
    • 切り取り終了点にカレントブランチを指定した場合、この段階では何も変化しない。
  2. 切り取り終了点から遡る全履歴コミットのうち、切り取り開始点から遡る全履歴に含まれていないコミットのリストである git log 切り取り開始点..切り取り終了点(便宜上、差分コミットと呼ぶ)を付け替え対象として退避する。
    • 切り取り開始点にブランチ名を指定した場合、そのブランチの最新コミットが開始点である。
  3. カレントブランチを接続先に強制リセットする
    git reset --hard 接続先
    • 切り取り終了点として指定したブランチの履歴はこの時点でリセットされてしまう。
    • 切り取り終了点ORIG_HEADに記録されるので、git reset --hard ORIG_HEAD にて復旧可能である。
  4. カレントブランチに対して、上記2で退避した差分コミットを順番に1つづつ再コミットしてブランチを伸ばしていく。
    • ただし、git log 切り取り開始点..接続先に含まれる各コミットと修正差分が同一のコミットはスキップされる。要するにcherry-pickで取り込んだ接続先と重複する内容は再コミットされない。
  5. 退避した差分コミットを全て処理したら動作完了です。

ここが肝心

上記2にて両者の履歴に共通して含まれないコミットを差分コミットとし、上記4にて重複する内容を再コミットしないことが、リベース動作を正しく理解するための肝です。

先の疑問の答え

先の疑問である「git rebase --onto main main current-branch切り取り開始点がmainで接続先もmainなので何も起きないのではないか」について答えます。

  • カレントブランチから遡る全履歴コミットのうち、切り取り開始点に指定したmainブランチの全履歴に含まれないコミットのリストが差分コミットとなる。それはカレントブランチがmainブランチから分岐した後のコミット履歴と等しい。
  • 接続先がmainブランチなので、mainブランチの最新コミットが接続先となる。
  • つまり、元のカレントブランチがmainブランチから分岐した後のコミット履歴を、mainブランチの最新コミットに接続したものが新しいカレントブランチとなるという、リベース動作になる。

豆知識

git rebase -i にてユーザに示されるコミット履歴は、上記4にて重複をスキップした差分コミットです。

切り取り開始点(upstream) 省略についての厳密な説明(読まなくて良いです)

省略時は切り取り終了点(branch)に設定されたリモートブランチ(上流ブランチ)の最新コミットを使う。

という説明は厳密には違います。
正確には切り取り終了点(branch)git branch --set-upstream-to= で設定された追跡ブランチが使われます。
そして追跡ブランチの設定が無い場合にはリベースは中止されます。

大半のケースでは git push -u を実行することにより「リモートブランチ」が追跡ブランチとして設定されているか、追跡ブランチが設定されていません。よって実質的には「リモートブランチ」を使うか、リベース中止になるかの二択となります。 git branch --set-upstream-to=main mybranch として追跡ブランチとしてローカルなmainブランチを設定すると git rebase main のかわりに git rebase とmainを省略することもできます。

具体例

カレントブランチを、mainの最新コミットから新規ブランチしたものとして再生成する.

git rebase main
  1. main..HEAD の差分コミットを退避し
  2. カレントブランチをmain最新コミットに強制リセットし
  3. 退避した差分コミットを適用する.

featXブランチを、mainの最新コミットから新規ブランチしたものとして再生成する.

git rebase main featX
  1. カレントブランチを featX に切り替え
  2. main..featX の差分コミットを退避し
  3. featX をmain最新コミットに強制リセットし
  4. 退避した差分コミットを適用する.

featXブランチを、tag3から新規ブランチしたものとして再生成する.

git rebase --onto tag3 main featX
  1. カレントブランチを featX に切り替え
  2. main..featX の差分コミットを退避し
  3. featX をtag3コミットに強制リセットし
  4. 退避した差分コミットを適用する.

featXブランチを、tag3から新規ブランチして featX~3..featX の内容で再生成する.

git rebase --onto tag3 featX~3 featX
  1. カレントブランチを featX に切り替え
  2. featX~3..featX の差分コミットを退避し
  3. featX をtag3コミットに強制リセットし
  4. 退避した差分コミットを適用する. featX~3以前のコミット main..featX~4 は適用されない.

参考資料

END

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5