📚

git rebaseって結局何ぞや

に公開

なんでもアウトプット(git rebase編)

git rebaseとは

  • あるブランチの変更(一連のコミット)を別のブランチの先端に移動させるコマンド。これによって、コミット履歴を線形で綺麗に保つことができる。

git merge との違い

git mergeは2つのブランチの変更履歴を統合し、新しい「マージコミット」を作成する。

  • ブランチの分岐と合流の履歴がそのまま残る
  • 既存のコミット履歴は変更されない

git rebase は、現在のブランチのコミットを一時的に退避させ、指定したブランチの先端にそれらのコミットを順番に適用し直す

  • コミットの親が変更されるため、既存のコミット履歴が書き換えられる
  • 複数のブランチからの変更が一本の線のように繋がって履歴が綺麗に見える
  • メインブランチへの追従を容易にし、コンフリクトを早期に発見・解消できる

Merge の場合

A---B---C (main)
    \
     D---E (feature)
        \
         F (current branch)

# マージ後
A---B---C---M (main)
    \       /
     D---E---F (feature)
        \   /
         G (current branch)  <-- マージコミット G が作成される

Rebase の場合

A---B---C (main)
    \
     D---E (feature)
        \
         F (current branch)

# rebase main した後 (feature ブランチ上)
      D'--E'--F' (feature)
     /
A---B---C (main)

# さらに current branch を feature に rebase した後
          F'' (current branch)
         /
      D'--E'--F' (feature)
     /
A---B---C (main)

主なユースケース

  • メインブランチへの追従: feature ブランチで作業中に、頻繁に更新されるメインブランチ (main, develop など) の最新の変更を取り込み、コンフリクトを早期に解消する。
  • コミット履歴の整理: 複数の細かいコミットをまとめたり、不要なコミットを削除したりして、より理解しやすい整理されたコミット履歴にする(インタラクティブ rebase)。
  • プルリクエストの整理: 他の開発者に見てもらう前に、feature ブランチのコミット履歴を整理し、レビューしやすい形にする。

使い方

4.1. メインブランチへの追従

作業中の feature ブランチに移動。

git checkout feature-branch

ベースとなるメインブランチの最新情報を取得。

git fetch origin main

現在のブランチをメインブランチの先端に rebase

git rebase origin/main

コンフリクトが発生した場合は、手動で解消し、ステージング後 git rebase --continue で再開。解消が難しい場合は git rebase --abort で中断する。

4.2. インタラクティブ Rebase によるコミット履歴の整理

整理したいコミットの親コミットを指定して、インタラクティブモードで rebase を開始。

git rebase -i HEAD~3

(直近 3 つのコミットを整理する場合)

エディタで表示されるコミットリストを編集し、以下のコマンドを使ってコミットを操作する。

pick: コミットを残す(デフォルト)。
squash (または s): 前のコミットと結合し、メッセージを編集する。
fixup (または f): 前のコミットと結合し、このコミットのメッセージを破棄する。
reword (または r): コミットメッセージを編集する。
drop (または d): コミットを削除する。

編集後、保存してエディタを閉じると、指定した操作に従って rebase が実行される。コンフリクトが発生した場合は、その都度解消が必要になる。


実際に git rebase -i HEAD~3をたたいてみた

pick 8ca119f 不要なコード削除
pick 8f3a4c2 不要なコード削除2
pick ef06798 不要なコード削除3

こう出てくるので

reword 8ca119f 不要なコード削除
fixup 8f3a4c2 不要なコード削除2
fixup ef06798 不要なコード削除3

こうしてあげると、1つ目のコミットに3つのコミットの変更がマージされて、コミットは1つとみなされる。
また1つ目のコミットにrewordを指定しているので、コミットメッセージを編集できる。

注意点

  • 公開された履歴の書き換えは避ける: リモートリポジトリにプッシュ済みのブランチに対して rebase を行うと、他の開発者のローカルリポジトリとの整合性が失われ、混乱を招く可能性がある。原則として、自分しか作業していないローカルのブランチに対してのみ rebase を行うべき。
  • 強制プッシュのリスク: やむを得ずリモートブランチを rebase した場合は、強制プッシュ (git push --force-with-lease origin <branch-name>) が必要になるが、これは他の開発者の作業を上書きする可能性があるため、非常に慎重に行う必要がある。

なぜプッシュ済みのブランチの rebase が危険なのか

Git は分散型のバージョン管理システム。リモートブランチの履歴を rebase で書き換えると、他の開発者がそのブランチを pull した際に、ローカルの履歴とリモートの履歴が矛盾してしまう。これにより、以下のような問題が発生する可能性がある。

  • 重複したコミット: 他の開発者が古い履歴に基づいて作業を続け、再度プッシュすることで、同じ変更が複数回反映されてしまう。
  • コミットの消失: 履歴の不整合を解消しようとする過程で、意図しないコミットが失われてしまう。
  • 作業の混乱: 履歴のずれを解消するために、多くの時間と労力がかかる。

参考記事

https://git-scm.com/book/ja/v2/Git-のブランチ機能-リベース
https://zenn.dev/ganariya/articles/git-rebase-operation

Discussion