
【Git】rebaseについて解説
2023.07.05
目次
rebaseとは
Gitに用意されているコマンドの一つになります。指定ブランチに連なっているコミットを別ブランチの最新コミットに続く形でつけかえる(rebase)ことができます。指定ブランチの内容を取り込むという意味ではgit mergeコマンドに似ていますが、rebaseにはmergeにないメリットがあるため使い分けられています。ただmergeと比べるとややこしい部分があるため、細かく解説していこうと思います。
解説
まずrebaseを実行した際にどういった変更がブランチに加わるのか図で見ていきます。
以下構成のブランチがあるとします。
1 2 3 | E---F---G feature / A---B---C---D develop |
developブランチ(以下develop)のBコミットから派生したfeatureブランチ(以下feature)のEコミットがあり、それにF、Gというコミットが連なっている状態です。
カレントブランチをfeatureとした状態で以下を実行します。
1 | git rebase develop |
もしカレントブランチがdevelopになっている場合は以下のコマンドでも上記コマンドと同様の結果にすることができます。(featureにチェックアウトした後rebaseを実行してくれる)
1 | git rebase develop feature |
するとブランチ構成は以下のように変更されます。
1 2 3 | E'---F'---G' feature / A---B---C---D develop |
派生の起点がBコミットからDコミット(developの最新コミット)に移動したことがわかります。
また、featureブランチのコミットにシングルクォーテーションがついたことに注目してください。
これはコミット番号が変わったことを表現しており、rebase前のブランチとは履歴上別コミットとして扱われます。
mergeとの違い
先程と同じrebase前のブランチ構成でmergeコマンドを実行してみましょう。
1 2 3 | E---F---G feature / A---B---C---D developカレントブランチをfeatureにした状態で実行。 |
1 | git merge develop |
すると以下のようになります。
1 2 3 | E---F---G---H feature / / A---B---C---D develop |
rebaseとの違いがはっきりわかりますね。mergeの場合、developのコミットを含めた新たなコミットHがfeatureに作成されます。また、B→Eの分岐点は変わっておらずE, F, Gのコミット番号はそのままになります。
コンフリクトの解消方法
コンフリクトの解消方法においてもrebaseとmergeで少し動きが異なります。コンフリクト箇所を修正するという点は共通ですが、rebaseの場合各コミットごとに適用していくことになります。具体的には、以下のブランチ構成だったとしてE, Fコミットはdevelopでの修正箇所と被っているとします。
1 2 3 | E---F---G feature (カレントブランチ) / A---B---C---D develop |
この状況でgit rebase developを実行するとまずEコミットがdevelopと競合がないか検証されます。今回Eコミットはdevelopと修正箇所が被っているというシチュエーションのためコンフリクトが発生します。
1 2 3 | E(コンフリクト)---F---G feature / A---B---C---D develop |
コンフリクトを解消するための修正を加えた後、対象のファイルを git add することでステージングします。これで一区切りになっており、次のFコミットをdevelopに適用するには以下のコマンドを実行します。
1 | git rebase --continue |
developに適用されていない残りのコミットがコンフリクトしなければここでrebaseが完了となりますが、今回Fコミットはdevelopと競合すると言う条件なので再びコンフリクトします。
1 2 3 | E'---F(コンフリクト)---G feature / A---B---C---D develop |
Eコミットのときと同じように対応し、コンフリクトを解消しステージングします。そして再度git rebase --continue
を実行することでrebaseが完了となります。(Gコミットはdevelopと競合していないため対応不要)
1 2 3 | E'---F'---G' feature / A---B---C---D develop |
これでコミット履歴が整理されましたが、rebaseにはコミットをまとめる機能が存在します。
これを利用し履歴を更に整理していきます。履歴がきれいになるイメージをしやすくするために上記記載のrebase後のブランチを再現してみました。
rebase したことにより、developの最新コミットであるCommitDの後に続く形でCommitE, F, Gが存在します。きれいな一直線になっていますね。ここからさらにCommitE, F, Gを1つにまとめることで、より一層履歴を見やすいものにすることができます。
そのためには以下のコマンドを実行します。
1 | git rebase -i HEAD~3 |
HEADの部分はgit log --oneline
コマンドを実行した際のHEADとなっているコミットから3つ分のコミットを示しています。ここで指定したコミット(E, F, G)をこれから1つにまとめていきます。
1 2 3 4 | git log --oneline f15ce0a (HEAD -> feature) Commit_G_プルリク指摘箇所修正 e8989b7 Commit_F_リファクタリング 5f1bb0a Commit_E_新規実装 |
rebaseコマンドの-iオプションを実行したことによりvimが開きます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 1 pick 5f1bb0a Commit_E_新規実装 2 pick e8989b7 Commit_F_リファクタリング 3 pick f15ce0a Commit_G_プルリク指摘箇所修正 4 5 # Rebase 6e558cf..f15ce0a onto 6e558cf (3 commands) 6 # 7 # Commands: 8 # p, pick <commit> = use commit 9 # r, reword <commit> = use commit, but edit the commit message 10 # e, edit <commit> = use commit, but stop for amending 11 # s, squash <commit> = use commit, but meld into previous commit 12 # f, fixup [-C | -c] <commit> = like "squash" but keep only the previous 13 # commit's log message, unless -C is used, in which case 14 # keep only this commit's message; -c is same as -C but 15 # opens the editor 以下省略 |
1行目から3行目を見てみると先程指定した3つのコミットが記載されており、先頭には「pick」というワードがあります。この「pick」を指定したコミットが、rebase先に適用する際に使用されます。今回はコミットをまとめたいのでコミットE以外は「pick」ではなく「fixup」を指定します。「fixup」は指定したコミットを直前のコミットに統合します。つまりCommitEにFとGが統合されひとまとめになります。
1 2 3 | 1 pick 5f1bb0a Commit_E_新規実装 2 fixup e8989b7 Commit_F_リファクタリング 3 fixup f15ce0a Commit_G_プルリク指摘箇所修正 |
同じようなコマンドとして「squash」があります。これもコミットを統合するコマンドですが、「fixup」との違いは統合する際のコミットメッセージの扱いです。「fixup」を指定した場合はコミットF, Gのメッセージは無視され、コミットEのメッセージが採用されます。「squash」は特に自分でいじらなければ元々のコミットメッセージが統合されます。
fixupした場合とsquashした場合のコミットログを抜粋したものが以下になります。squash
の方は統合前のコミットメッセージが残っていますね。
1 2 3 4 5 6 7 8 9 | // fixup Commit_E_hoge機能追加対応 // squash Commit_E_hoge機能追加対応 Commit_F_hoge機能追加対応 Commit_G_hoge機能追加対応 |
コミットを統合しているという意味はfixup
もsquash
も同じなので、プロジェクトの方針やお好みに合わせて使い分ければいいと思います。
これでコミットを整理することができました。ログを確認するとdevelopの最新コミットであるコミットDの後に、先程統合したコミットEが並んでる形になっています。
1 2 3 | git log --oneline 9c10639 (HEAD -> feature) Commit_E_新規実装 6e558cf (develop) Commit_D_hoge機能追加対応 |
まとめ
今回はrebaseの使い方について紹介しました。今までmergeしか使ってこなかったので、最初は手こずりましたが慣れるとコミットログが整理されて見やすいため便利に感じました。ただ後々ログを見返すことがない場合やプロジェクトの方針で特に使用する必要がない場合はmergeの方が楽なのでどちらを使うかはケースバイケースかなと思います。
書いた人はこんな人

- フルスタックエンジニアとして活躍することを目指す新米エンジニア。
現在はバックエンドを担当しており、日々奮闘中。
プログラムを組んで課題を解決することが好きなため
休日も開発に勤しむことも、、、
IT技術10月 26, 2023JPAの仕様を洗っていく
IT技術7月 3, 2023【Git】rebaseについて解説
IT技術2月 1, 2023ChatGPTを使ってCTF解いてみた