Git Rebase –onto: プロフェッショナルなGit履歴操作の極意

Git tutorial - IT technology blog
Git tutorial - IT technology blog

5分でマスターする Git Rebase –onto

feature-Aをベースにfeature-Bを開発してしまったものの、後からfeature-Aがキャンセルされたり、履歴が複雑になりすぎて困ったことはありませんか?そんな時、古いブランチの混乱に巻き込まれることなく、自分のコミットだけをmainに持っていきたいと思うはずです。一つずつcherry-pickするのではなく、git rebase --ontoを使えば一瞬で解決できます。

このような状況に直面したら、この「魔法の」構文を思い出してください:

bash
git rebase --onto <ターゲットブランチ> <古いベースブランチ> <現在のブランチ>

例えば、現在feature-Bにいて、feature-Aを飛ばして直接mainに移動したい場合は、次のように実行します:

bash
git rebase --onto main feature-A feature-B

このコマンドは:「feature-Bのコード(feature-Aに既に含まれているものを除く)を取り出し、それをmainの先端に配置する」という意味です。たった一行のコマンドで、Gitの履歴をまるで最初からそうであったかのようにスムーズに整理できます。

–onto の仕組みを解明する

なぜこのコマンドがこれほど強力なので状況でしょうか? その秘密はGitのコミット選択方法にあります。通常のrebaseはブランチ全体を移動させますが、--ontoを使用すると、移動させるべきコミットの範囲を正確に指定できるからです。

詳細な構造は、次の3つの要素で構成されています:

  • newbase: 新しいベース地点(コミットを再配置したい場所)。
  • upstream: Gitが不要な部分を特定するための目印。この地点以前のコミットはすべて無視されます。
  • branch: 移動させたいブランチ(現在そのブランチにチェックアウトしている場合は省略可能です)。

多くの人はcherry-pickを使って手動で一つずつコミットを選ぶ方法をとります。その方法も間違いではありませんが、コミットが15〜20個もある場合、非常に時間がかかり、ハッシュ値を間違えるリスクも高まります。

実践シナリオ:機能ブランチが重なった場合

よくある次のようなコミット図を想定してみましょう:


o---o---o (main)
     \
      o---o (feature-A)
           \
            o---o---o (feature-B)

背景:feature-Aはまだ開発途中ですが、急遽feature-Bだけをmainに取り込んで顧客にデモを見せる必要が出てきました。通常のrebase mainを使うと、feature-Aの未完成なコードまでmainに引き込んでしまいます。これは避けたい事態です。

--ontoによる解決策:

bash
git rebase --onto main feature-A feature-B

結果は次のようになります:


o---o---o (main)
     |   \
     |    o'---o'---o' (mainから新しく派生したfeature-B)
      \
       o---o (元の場所に残ったfeature-A)

feature-Bからfeature-Aの痕跡が完全に消えました。これは、リーダーにプルリクエスト(Pull Request)を送る前にコミット履歴をきれいにするための最良の方法です。

高度なテクニック:ブランチ途中の誤ったコミットを削除する

もう一つの非常に便利な使い道は、rebase -iを使わずに途中のコミットグループを削除することです。例えば、A -> B -> C -> D -> Eというコミットの流れがあり、BCに誤ってAPIキーや機密パスワードが含まれてしまったとします。

bash
git rebase --onto <Aのハッシュ> <Cのハッシュ> master

このコマンドはGitに「Cより後のコミット(つまりDE)を取り出し、それらを直接Aに繋ぎなさい」と指示しています。数秒で機密データを履歴から排除できました。

実体験に基づくアドバイスと安全上の注意

rebaseを使用するということは、履歴を書き換える(rewrite history)ことを意味します。次のルールを肝に銘じておいてください:**すでにサーバーにpushされ、他のメンバーと共有しているブランチでは決してrebaseを行わないこと。**

rebase後にどうしてもpushが必要な場合は、次のコマンドを使う習慣をつけましょう:

bash
git push --force-with-lease

このコマンドは--forceよりも安全です。なぜなら、サーバー側に自分がまだ知らない新しいコミットがある場合、pushを拒否してくれるからです。これにより、同僚の作業を誤って上書きしてしまうという、チーム開発で非常によくある致命的なミスを防ぐことができます。

ヒント: 複雑なGit操作を行う前には、git branch backup-branchでバックアップ用ブランチを作成しておきましょう。万が一失敗しても、問題のあるブランチを削除してバックアップの名前を戻すだけで済みます。安全第一です!

手順のまとめ

  1. 3つのポイント(移動先: newbase、切り取りポイント: upstream、移動するブランチ)を特定する。
  2. 念のためバックアップブランチを作成して逃げ道を作る。
  3. git rebase --ontoコマンドを実行する。
  4. コンフリクト(conflict)が発生した場合は解消する。新しいベースに移動する際のコンフリクトは極めて一般的です。
  5. git log --oneline --graphで結果を確認する。
  6. --force-with-leaseでpushする。

このガイドが、複雑なブランチ構造を扱う際の自信に繋がれば幸いです。git rebase --ontoは決して難しくありません。「どこから切り取って」「どこに貼り付けるか」を明確にイメージするだけです。綺麗なコミット履歴を目指して頑張りましょう!

Share: