😸

git rebase について具体例を用いてうれしさをまとめる

2023/08/11に公開

はじめに

先日職場において、とある話題がきっかけで git rebase の話題になりました。
このとき ganyariya は「git rebase よくわかってないな...」となりました。

そのため、今回は git rebase について具体例をもちいてまとめようと思います。

具体例

ganyariya はノベルゲーム専門のオンライン通販ショップ ノベストア を開発しています。
ノベストアでは、さまざまなノベルゲームを通販で販売しています。
ノベストアの開発言語は php7.4 であり、セキュリティの都合上 php8 系へアップデートすることを考えています。

今後のノベストアの開発においては、以下 2 つを並行して進めることにしました。

  • php のバージョンをアップデートする
  • HTML5&JavaScript ノベルゲームをオンラインで作成・共有できる機能を作成する
    • オンラインでプレイできるノベルゲームをサイト上に追加することで、ユーザ数を増やしたい

git rebase

現在、ノベストアはリリース済みであり、コミットするたびに自動テストが行われるようになっています。


# main から checkout
git checkout -b feature/php8-update

ganyariya-A さんは php8 のアップデートブランチを作成し、アップデートの準備をしています。
はじめは php8.0 にアップデートする予定でしたが、できるだけあげようという方針に変更され php8.2 へ上げることにしました。


# main から checkout
git checkout -b feature/online-novel-game-share

ganyariya-B さんは、 php8 アップデートではなく、HTML5 ノベルゲームをオンラインで公開できる機能を作成しました。
php8 アップデート作業は難航している、かつ失敗すると事業的影響が大きいため、先にオンライン公開機能を本番リリースすることになりました。


git checkout main
git merge feature/online-novel-game-share

main ブランチに feature/online-novel-game-share をマージして本番デプロイしました。


ganyariya-A さんは、まだまだ php8 アップデートの作業が残っています。
CI/CD を php8 対応させたり、 Dockerfile も php8 対応させる必要があります。

ここで、 ganyariya-A さんは main ブランチ側で「オンライン公開機能」がすでにリリースされていることに気づきました。
上記の機能を php8 ブランチに取り込み、 php8 ランタイムでもうまく動くか確認する必要があります。

このとき、普段つかっている merge にくわえて、 rebase で取り込む方法も試してみることにしました。

git checkout feature/php8-update
git checkout -b feature/php8-update-merge
git checkout -b feature/php8-update-rebase

git checkout feature/php8-update-merge
git merge main

まずは普段使っている merge の方法です。

feature/php8-update-mergemain を取り込んだところ、マージコミット 8456b075 が作成されました。

feature/php8-update-merge のコミット履歴を見ると、php8 アップデート がより過去にコミットされており、そののちに ノベルゲーム公開機能 がコミットされています。

歴史的経緯としては php8 アップデートを ganyariya-A が行ってから、 ganyariya-B が ノベルゲーム公開機能 を行っているため正しいです。


しかし、今後も php8 アップデート作業が長期化し、その間にリリースされたノベストア機能コードを feature/php8-update-merge へマージするたびにコミット履歴が汚くなります。
上記 2 枚の画像では、ノベストア機能コードを適宜 feature/php8-update-merge にマージしていったときのコミット履歴です。

php8 と機能開発コミットが時系列順に混ざって表示されており、どのコミットが php8 に関するものなのか分かりづらくなってしまいます。


一方で、 rebase では re base (ベースをつけかえる) という名の通り、コミット履歴を改ざんします

git checkout feature/php8-update-rebase
git rebase main

feature/php8-update-rebase で行われていたコミット操作が あたかも main ブランチ上で作業していた かのようにコミットが付け替えられます

もともとの php8 アップデートは 99e99aa6 から 21fb92b9 のコミットでした。
これを main 側につけかえることによって、あらたに 0956a274 から a17e97f8 というコミットが git 側で自動作成されています。

この自動コミット付け替えによって、あたかも オンライン公開機能が作成されてから php8 アップデート作業をはじめた かのようなコミット履歴を作成できます。

ノベストア機能開発がどんどん進んでいたとしても、 上記の画像のように rebase できれいなコミット履歴を作れます。

git rebase -i

rebase 操作でよく使う git rebase -i についても記載します。

php8 アップデート作業を ganyariya-A が行っていたところ、ローカル環境における phpunit テストが落ち続けるようになりました。
phpunit テストが落ち続ける原因はなんなのか、色々な箇所をコミットしながらテストが動くまで試行錯誤しました。
結果として、phpunit に与えるメモリが足らなかったとわかり、メモリを多く与えることで解決しました。

しかし、ここまでの試行錯誤においてコミット履歴がだいぶ汚れてしまいました。
そこで、直近の phpunit 試行錯誤のコミットを git rebase -i できれいにすることを目指しました。


git checkout feature/php8-update-rebase
git checkout -b feature/php8-update-rebase-i
git rebase -i main

git rebase -i mainfeature/php8-update-rebase-i のコミットを main 上であたかも行ったように見せかけるコミット改ざんを行います。
また、 -i (interactive) オプションによってコミットごとにどのように操作するかを指定できます。

今回は 761944342a8596 で行った操作を ed2e088 コミットに squash (押しつぶして混ぜる)ことにしました。
これによって、README の追加や php8.2 へのアップデートを 1 つのコミットにまとめられます。

また、 unittest 系の試行錯誤は必要ないと判断したため、 drop でコミットを削除することにしました。

047374ereword を指定することで、 phpunit に与えるメモリを増やす というコミットメッセージへ修正することにしました。

このように rebase -i を指定することで、単純に base を付け替えるだけでなく、どのコミットを利用するのかなどを編集できます。
これによって、コミット履歴をよりきれいに保てます。

注意

ここまでの通り、 git rebase はコミット履歴を改ざんする操作になっています。
そのため、すでに GitHub の PullRequest に branch を push しているなど、他の人と共有するブランチを rebase するのは危険が高いです。
なにかコンフリクトした場合、うまく解決する必要が出てきます。

安全に rebase をしたいのであるならば、自分のローカル環境のみで rebase を行い、 origin 上では行わないのが安全かと思います。

自分の場合は rebase する場合、以下にするように気をつけています。

  • 他の人の commit は rebase しない。 rebase 対象は自分のコミットのみ。
  • 一度でも origin に push したものは rebase しない。
  • つまり、 rebase していいものは自分のコミット、かつ origin に push していないもの。

https://www.zoma-blog.com/post/other/git/20230222_git_rebase_avoid/

参考リンク

https://git-scm.com/book/ja/v2/Git-のブランチ機能-リベース

https://zenn.dev/tana0102/articles/475d8952933af6

https://style.biglobe.co.jp/entry/2022/03/22/090000

GitHubで編集を提案

Discussion