【ITニュース解説】git rebase and squash before opening merge requests
2025年09月17日に「Dev.to」が公開したITニュース「git rebase and squash before opening merge requests」について初心者にもわかりやすく解説しています。
ITニュース概要
チーム開発でGitを使う際、マージリクエスト前にきれいなコミット履歴を作るには、`git rebase -i`で不要なコミットをまとめよう。まずメインブランチを最新にし、作業ブランチのコミットを整理後、リモートへプッシュする。競合に注意し、クリーンな開発を心がけよう。
ITニュース解説
ソフトウェア開発の現場では、個人で黙々とコードを書くよりも、複数の開発者が協力して一つのプロジェクトを進めることが一般的である。個人開発の段階では、自分の書いたコードを直接「メイン」のコードに反映させたり、あるいは簡単な開発用ブランチを使ってからメインブランチに統合したりする比較的単純な方法で事足りるかもしれない。しかし、チームで開発に取り組むようになると、コードのバージョン管理システムである「Git(ギット)」の使い方がより洗練され、チームとしての「ベストプラクティス」、つまり最も推奨される手順が求められるようになる。新しいチームに参加した際には、まずそのチームがどのようにGitを運用しているのか、ブランチの命名規則やコードをメインブランチに統合する際の手順(マージリクエストやプルリクエストの運用など)をしっかりと把握することが非常に重要となる。
Gitを用いてコードをメインブランチに統合する際、主に二つの主要な方法が存在する。「git rebase(リベース)」と「git merge(マージ)」である。これらの違いを理解することは、チーム開発を円滑に進める上で不可欠だ。git mergeは、異なるブランチで行われた変更を一つにまとめる際に、「マージコミット」と呼ばれる特別なコミットを作成する。このマージコミットは、どのブランチがいつ、どのように統合されたかという履歴をそのまま記録するため、ブランチの分岐と統合の経緯が視覚的にわかりやすくなる。対照的に、git rebaseは、自分のブランチで行われたコミット群を、まるで最新のメインブランチの先端で最初から行われたかのように「歴史を書き換える」操作だ。具体的には、自分のブランチのコミットを一時的に取り外し、そのブランチの元になったメインブランチの最新の状態の上に、改めて自分のコミットを適用し直す。この結果、コミット履歴は一直線に繋がり、非常に整然として見やすくなる。多くの開発チームでは、コード履歴の可読性を高めるためにrebaseを選択することが多いが、オープンソースプロジェクトのように、すべての履歴の分岐を詳細に残すことが重要視される場合にはmergeが選ばれることもある。
次に、rebaseと「squash(スカッシュ)」という操作を組み合わせて、自分のコードをメインブランチに統合する具体的な手順を解説する。squashとは、複数の細かいコミットを一つの意味のあるまとまったコミットに統合する操作であり、これも履歴をきれいに保つために役立つ。
まず最初に、作業を始める前、あるいはrebaseを実行する直前に、ローカル環境にあるmainブランチ(メインブランチ)を、リモートリポジトリ(他の開発者と共有している中央のコード保管場所)の最新の状態と同期させる必要がある。これは、チームの他のメンバーがすでに新しいコードをリモートのmainブランチにマージしている可能性があるためだ。常に最新のmainブランチの上に自分の変更を適用することが、スムーズな統合の鍵となる。以下のコマンドは、リモートリポジトリ(ここでoriginは慣習的にリモートリポジトリを指す)のmainブランチの変更を、ローカルのmainブランチに取得するものである。
git pull origin main
次に、rebaseを行いたい、つまりメインブランチに統合したい自分の作業ブランチに切り替える。例えば、feature/my-new-featureという名前のブランチで作業していた場合は、以下のコマンドでそのブランチに移動する。
git checkout [自分のブランチ名]
いよいよrebaseの核心部分である。rebaseを対話モードで実行し、自分のブランチ内にある複数のコミットを整理する。対話モードとは、Gitが一つ一つのコミットに対してどのような操作を行うかをユーザーが指示できるようにするモードのことだ。以下のコマンドを実行する。
git rebase -i main
このコマンドに含まれる-iというフラグが「インタラクティブ(対話)」モードを意味する。コマンドを実行すると、ターミナル内にテキストエディタが開き、自分のブランチにあるコミットが一覧表示される。通常、各コミットの行の先頭には「pick(ピック)」という文字が書かれているはずだ。「pick」は、そのコミットをそのまま残すという意味である。ここで、キーボードの「i」キーを押してEnterキーを押すと、エディタが「挿入モード」に入る。挿入モードでは、カーソルを動かして「pick」の部分を別の指示に書き換えることができる。例えば、小さな修正やコメントの追加など、あまり重要でない複数のコミットを、その前のコミットに統合したい場合は「squash」に書き換える。これは、複数のコミットを一つにまとめる際に用いる。また、コミットメッセージ自体を修正したい場合は「reword(リワード)」に書き換える。一般的に、一覧の一番上に表示されている最も古いコミットは「pick」のままにしておくのが良い。全ての編集が終わったら、「ESC」キーを押して挿入モードを終了し、続けて「:wq」と入力してEnterキーを押す。これは、変更を「書き込み(write)」、そしてエディタを「終了(quit)」するという意味である。
この操作が成功すると、ターミナルにはブランチがリベースされたことを示すメッセージが表示されるはずだ。これにより、自分のブランチのコミット履歴が、最新のmainブランチの変更の直後に追加されたような形になり、履歴が直線的に整理される。ただし、この変更はまだローカル環境の自分のブランチにのみ適用されている点に注意が必要だ。リモートリポジトリにはまだ反映されていない。コミットがsquashされたことを確認するには、以下のコマンドでコミット履歴を簡潔に表示できる。
git log --oneline
ローカルでのrebaseとsquashが完了し、コミット履歴がきれいになったら、その変更をリモートリポジトリの自分のブランチにプッシュする。
git push origin [自分のブランチ名]
このコマンドを実行すると、ローカルのブランチの変更がリモートにアップロードされる。すると、GitLabやGitHubのようなバージョン管理プラットフォームでは、「[ブランチ名]にX秒前にプッシュされました。マージリクエスト(プルリクエスト)を作成しますか?」といった通知が表示されるはずだ。これは、リモートリポジトリが、あなたのリモートブランチに変更が加えられたことを検知したためである。
通知に従い、マージリクエストを作成する。マージリクエストは、自分の変更をメインブランチに統合してほしいという、一種の「提案」のようなものだ。チームのメンバーがあなたのコードをレビューし、自動テスト(CIパイプラインと呼ばれることが多い)がすべて正常にパスし、最終的な承認が得られたら、マージリクエストの「マージ」ボタンを押す。これにより、あなたのリモートブランチの変更が正式にリモートのmainブランチに統合される。マージ後には、使用した作業ブランチを自動的に削除するオプションが提供されていることが多いので、これにチェックを入れておくことで、ブランチが散らかるのを防ぎ、リポジトリをきれいに保つことができる。
自分の変更がリモートのmainブランチにマージされた後も、まだ対応すべきことがある。今度は、ローカル環境にあるmainブランチが、リモートのmainブランチに反映された最新のコード(あなたの変更も含む)と同期していない状態になっている。再びgit pull origin mainを実行して、ローカルのmainブランチをリモートの最新の状態に戻しておく。
git pull origin main
そして最後に、作業が完了し、メインブランチに統合されたブランチは不要になるため、ローカル環境から削除することができる。新しいタスクや機能開発に取り掛かる際は、また新しいブランチを作成して作業を進めるのが、チーム開発における一般的なワークフローである。
チームでコード開発を行っていると、どうしても避けられないのが「コンフリクト(衝突)」の発生である。コンフリクトは、複数の開発者が同じファイルの同じ行を、互いに異なる内容で変更した場合などに発生する。コンフリクトが発生する主なタイミングは二つ考えられる。一つは、自分のブランチをローカルでrebaseしている最中だ。もし、あなたが作業しているコードの箇所が、あなたがブランチを切った後で他の誰かがmainブランチにマージした変更と重なっている場合、rebaseの途中でコンフリクトが発生する。例えば、HelloWorldという関数の一部を変更したとして、rebaseを実行しようとしたら、別の同僚がすでにmainブランチに同じ行のHelloWorld関数に別の修正をマージしていた、といったケースがこれにあたる。もう一つは、最終的に自分のブランチをmainブランチにマージしようとした時だ。あなたがrebaseを完了し、自分のリモートブランチを更新した後でも、もしその間に他の誰かがmainブランチに、あなたの変更と重なるようなコードをマージしていた場合、最終的にあなたのブランチをmainにマージしようとした時にコンフリクトが発生する可能性がある。コンフリクトが発生した場合は、Gitが示す衝突箇所を手動で編集し、どちらの変更を残すか、あるいは両方の変更をどのように統合するかを自分で決定する必要がある。
結論として、git rebaseはコミット履歴をきれいに、かつ直線的に保つための非常に強力なツールである。これにより、後から履歴を追う際に、いつ、どのような変更が行われたのかが非常に分かりやすくなる。しかし、rebaseによってローカルで行った履歴の書き換えは、git pushでリモートリポジトリにプッシュするまでは、他の開発者からは見えないローカルな変更に過ぎないことを常に意識しておく必要がある。また、作業の開始前と終了後の両方で、ローカルのmainブランチをリモートのmainブランチと必ず同期させておくという習慣も極めて重要だ。これは、常に最新のコードベース上で作業し、不必要なコンフリクトの発生を未然に防ぐために大いに役立つ。これらのgitの適切な運用を身につけることは、システムエンジニアとしてチーム開発に参加する上で必須のスキルとなるだろう。