git rebaseとは?「使い方」などを図を用いて分かりやすく解説!

この記事では『git rebaseコマンド』について、

  • git rebaseとは
  • git rebaseの使い方
    • git rebaseして、作業ブランチを最新の状態に保つ手順
  • git rebaseのメリット
  • git rebaseの注意点
  • リベース(rebase)とマージ(merge)との違い

などを図を用いて分かりやすく説明するように心掛けています。ご参考になれば幸いです。

git rebaseとは

git rebaseとは

git rebaseはあるブランチの基点(ベース)を新しい基点に再設定するGitコマンドです。

通常、Gitでブランチを作成すると、そのブランチには基点となるコミットがあります。例えば、上図の場合、featureブランチの基点はコミットAになります。featureブランチをmainブランチにリベースすると、featureブランチの基点がmainブランチの最新コミットEの後に移動されます。その結果、mainブランチの最新の状態をfeatureブランチに取り込むことができるようになります。

その他にもgit rebaseはコミットメッセージをまとめたり、コミットメッセージを修正したりする際にも使用することができます。

Gitにおけるリベース(rebase)は"Re Base"、すなわち「ベースを再設定する」という意味があります。

git rebaseの使い方

これからgit rebaseコマンドを用いてブランチの基点(ベース)を変更し、最新の状態に保つ方法を説明します。

例えば、新機能を追加するために、mainブランチからfeatureブランチを作成して、開発を進めたとします(下図ではコミットAからfeatureブランチを作成しています)。この場合、featureブランチの基点はコミットAになります。そして、featureブランチでの作業でコミットを2回(B, C)したとします。すると、ブランチの状態は以下のようになります。

git rebaseの使い方01

これをGitHubやCode Commit等にプッシュするには、以下のコマンドを実行します。

git push origin feature

上記のコマンド実行後にプルリクエストを出すことが多いと思います。しかし、チームで開発をしていると、他のメンバーの作業によって、以下のようにfeatureブランチの作業中にmainブランチが更新されることが多いです。例えば、mainブランチで2回更新(コミットD,コミットE)された場合、ブランチの状態は以下のようになります。

git rebaseの使い方02

この場合、mainブランチの最新状態をfeatureブランチに取り込んでからプッシュしないと、コンフリクトが起きる可能性があります(次にmainブランチの最新状態をfeatureブランチに取り込む方法を説明します)。

git rebaseして、作業ブランチを最新の状態に保つ手順

mainブランチの最新状態をfeatureブランチに取り込む手順を以下に示します。mainブランチが最新状態の場合、手順1と手順2は省略しても構いません。

手順

  • 作業ブランチでの作業が終わったら、ブランチをmainブランチに戻す
  • mainブランチを最新の状態にする
  • ブランチを作業ブランチに戻す
  • git rebaseを実行する

作業ブランチでの作業が終わったら、ブランチをmainブランチに戻す

作業ブランチ(今回はfeatureブランチ)での作業が終わったら、以下のコマンドを実行して、ブランチをmainブランチに戻します。

git checkout main

mainブランチを最新の状態にする

mainブランチに切り替えたら、以下のコマンドを実行して、リモートリポジトリ上のmainブランチの最新状態を取り入れます。

git pull

ブランチを作業ブランチに戻す

mainブランチを最新の状態にしたら、以下のコマンドを実行して、元の作業ブランチ(今回はfeatureブランチ)に切り替えます。

git checkout feature

git rebaseを実行する

mainブランチの最新状態をfeatureブランチに取り込むため、以下のコマンドを実行します。

git rebase main

git rebase mainを実行すると、ブランチが以下のようになり、featureブランチの基点がmainブランチの最新コミットEの後に移動され、mainブランチの最新状態をfeatureブランチに取り込むことができるようになります。

git rebaseして、作業ブランチを最新の状態に保つ手順

その後、以下のコマンドを実行して、GitHubやCode Commit等にプッシュします。

git push origin feature

補足

今回はmainブランチの最新状態をfeatureブランチに取り込みましたが、プロジェクトによっては別ブランチ(例えば、masterブランチやdevelopブランチ)の最新状態をfeatureブランチに取り込む場合があります。その際には、mainの箇所を別ブランチの名前(masterdevelop)に変えてください。

プルリクエストを出す前にgit rebaseを使って、featureブランチを最新の状態に保つことで、プルリクエスト時の競合を解決することができますし、コミット履歴が見やすくなるのでお勧めです。

git rebaseのメリット

git rebaseを使用すると、どのようなメリットがあるのか、GUI上で実際に確認してみましょう。以下のmainブランチとfeatureブランチを例にして説明します。

git rebaseのメリット01

mainブランチはfeatureブランチより少し進んでいる状態です。この状況で、git rebaseのメリットについて説明します。

履歴が見やすくなる

git rebaseのメリット02

featureブランチでgit merge mainを実行し、mainブランチをマージ(merge)した場合、マージコミットが作成されてしまいます。例えば、featureブランチの作業中に10回mainブランチを取り込もうとした場合には、マージコミットが10回も作成されてしまいます。また、上図から分かるように、GUI上で確認すると、featureブランチとmainブランチの線が重なり、コミット履歴が複雑になります。

一方、featureブランチでgit rebase mainを実行し、mainブランチをリベース(rebase)した場合、マージコミットが作られず、履歴が一直線に並ぶため、変更履歴が見やすくなります。

最新のブランチを取り込んでコミットできる

git rebaseのメリット03

mainブランチをfeatureブランチにマージ(merge)した場合、mainブランチとfeatureブランチのコミットが時系列順に並びます。そのため、featureブランチで機能追加後にマージを行うと、featureブランチの機能追加後にmainブランチの変更コミットが入ってきます。上図の例だと、featureブランチで[feature]B[feature]Cのコミットにより機能を追加したのに、マージをしたことで、mainブランチでの変更コミット([main]D[main]E)が入ってきてしまいます。

また、マージ後のfeatureブランチにおいて、[feature]B[feature]Cのコミットにより追加した機能が正常に動作するかが不安になります(mainブランチでの変更により、featureブランチの機能追加に影響がある可能性があるため)。

また、featureブランチの機能追加が終わると、featureブランチをmainブランチにマージすると思いますが、mainブランチにマージ後も変更履歴が見にくいです。

一方、featureブランチでmainブランチをリベース(rebase)した場合、featureブランチの基点がmainブランチの最新コミットEになるため、featureブランチでは常にmainブランチの最新の変更を取り込んで機能を追加することができます。これにより、mainブランチにfeatureブランチでの変更を取り込む前に、featureブランチで行った変更による動作が、mainブランチに加えられた変更(コミット)によって、おかしくなっていないかを確認することができます。

また、featureブランチをmainブランチにマージした後も変更履歴が見やすいです。

1つ1つのコンフリクトが小さい

git rebaseのメリット04

マージ(merge)では、ブランチ同士の最終的な差分でコンフリクトがないかを確認します。上図の場合、featureブランチとmainブランチでコンフリクトがないかを確認しています。そのため、場合によっては大きなコンフリクトが発生することがあり、コンフリクトの解消が大変になります。

一方、リベース(rebase)では、1つ1つのコミットに対してコンフリクトを検出し、コンフリクトがあればその都度コンフリクトを解消します。結果として、コンフリクトが小さく済みます。上図の場合、まずfeatureブランチのコミットBとmainブランチでコンフリクトがないかを確認します。コンフリクトがあれば、コンフリクトを解消します。次に、featureブランチのコミットCとコミットB'を含んだ状態のmainブランチでコンフリクトがないかを確認します。コンフリクトがあれば、コンフリクトを解消します。

このようにリベースでは、各々のコミットに対してコンフリクトを解消していくようになっています。

git rebaseの注意点

リベース(rebase)は、実際にはコミットを移動しているのではなく、既存のコミットを破棄して、新しいコミットを作成しています。新しく作成されたコミットは見た目は元のコミットと似ていますが、実際には別のコミットです。そのため、コミットのハッシュ値(コミットID)が変わります。

git rebaseの注意点

このようにリベース(rebase)はコミット履歴を書き換える操作であるため、公開リポジトリや共有プロジェクトで使用する際には注意が必要です。そのため、「git push をした後やプルリクエストを出した後には git rebase をしてはいけない」というのが、Gitのリベース(rebase)の説明によく出てきます。他のメンバーがリベース前のコミットを基点にして作業していると、git rebase によりその基点となるコミットが破棄されてしまい、整合性が取れなくなることがあるからです。

リベース(rebase)とマージ(merge)との違い

今まで説明した内容と重複する点もありますが、リベース(rebase)とマージ(merge)の違いを以下にまとめます。

  • コミット履歴の見やすさ
    • リベース(rebase)
      • コミット履歴が直線的になるため、コミット履歴が見やすい。
    • マージ(merge)
      • マージコミットが作成されるため、コミット履歴が複雑になることがある。
  • コンフリクトの大きさ
    • リベース(rebase)
      • 1つ1つのコミットに対してコンフリクトを解消するため、コンフリクトが小さい。
    • マージ(merge)
      • ブランチ同士の最終的な差分でコンフリクトを解消するため、コンフリクトが大きくなる可能性がある。
  • 既存のコミットを破棄するか否か
    • リベース(rebase)
      • 既存のコミットを破棄して新しいコミットを作成するため、コミットIDが変わる。
      • そのため、公開リポジトリや共有プロジェクトで使用する際には注意が必要。
    • マージ(merge)
      • 既存のコミットをそのまま残すため、コミットIDは変わらない。
      • そのため、公開リポジトリや共有プロジェクトで安全に使用できる。

本記事のまとめ

この記事では『git rebaseコマンド』について、以下の内容を説明しました。

  • git rebaseとは
  • git rebaseの使い方
    • git rebaseして、作業ブランチを最新の状態に保つ手順
  • git rebaseのメリット
  • git rebaseの注意点
  • リベース(rebase)とマージ(merge)との違い

お読み頂きありがとうございました。