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