はじめに
Gitでmainブランチに複数のブランチをマージした際に「想定外のコミット履歴」となってしまい、「mainブランチに必要なコミットが反映されない」事象を確認しました。筆者のGitに関する知識不足が一番の原因で、Gitに詳しい人であれば(そしてよくよく考えれば)「そんなの上手く行かなくて当たり前じゃん!」という内容ですが、備忘録として本記事に書き残します。
今回ご紹介する事象の概要をお伝えすると、
「あるブランチのコミット履歴を git rebase [別のブランチ]により(今までとは異なる)別のブランチから分岐するように更新した際、条件によってはrebase元に存在していたコミットが反映されずにスキップされる」
というものです。
以下具体的に説明していきます。
「必要なコミットが反映されない」事象発生の経緯
当初の予定
以下のようにmainにマージするbranch1を作成して、素直にmainブランチにマージする予定でした。
予定変更
ここで「branch1のコミットBだけ先にmainブランチにマージしたい」事情が発生し、次のような修正を行います。
-
branch1のコミットBをgit revertして取り消す
git checkout branch1
git revert --no-edit [BのコミットID]
- 新たに
branch2をmainからの分岐で作り、コミットBをbranch2にcherry-pickする
git checkout -b branch2 main
git cherry-pick [BのコミットID]
ここまででリポジトリは以下のような状態になっています。

この後、先にbranch2をmainにマージしてM2のコミットを作り、次にbranch1をマージしてM1の状態にする予定でした。そして適用順序は変わっても、当初予定していたbranch1のA,B,Cのコミットが全てmainブランチに反映される想定でした。
実際に上図のようなコミット履歴でM1にマージしたら、A,B,C全てmainブランチに反映されますので(おそらくですが)ご心配なく。
branch1をそのままマージできない...
ところがここで想定外の事態が発生します。
branch2をmainブランチにマージした状態で、さらにbranch1をmainにマージするのがNGであることが判明するのです。
実際にNGだったのはリポジトリを載せていたGitLabの設定によるものだったのですが...、その説明は割愛させてください。本記事では「何らかの事情でbranch1をmainブランチからrebaseせざるを得なくなった」と思って読んでいただければとm(_ _)m
そこで仕方なく「branch1をrebaseして、M2のコミット(mainブランチの現在位置)から分岐する」作戦を取ります。
「上図のようにrebaseしてmainブランチにマージしても、もともと予定していたbranch1のA,B,Cのコミットが全て反映されるだろうなあ」と、この時点では当然のごとく考えていました。Bはbranch2で一回pBとして反映されててbranch1ではB'とrB'で打ち消されるから「1+(1-1)=1」で1回分のB相当のコミットは残るよねー、的な安易な想定で。
この後「Gitのコミットは算数の加減算で簡単に理解できるようなものじゃないんだぞ!!」と思い知らされます。。。
「git rebase」で必要なコミットがスキップされる...
そしていざrebase!
git checkout branch1
git rebase main
rebase後のmainブランチの中身を見てみると、おかしなことに気づきます。
あれ、コミットBの中身が消えてなくないっ!?
はい、まさにそうなのです。。。ターミナルなどでgit rebase時の出力を見ると(GitLab等のWebUI上では見れないかもしれないですが)、以下のようなwarningが出てコミットBがスキップされていることを確認できます。
$ git rebase main
warning: skipped previously applied commit [BのコミットID]
hint: use --reapply-cherry-picks to include skipped commits
hint: Disable this message with "git config advice.skippedCherryPicks false"
Successfully rebased and updated refs/heads/branch1.
筆者環境だと、hintを参考にgit rebase main --reapply-cherry-picksしてもコミットBがスキップされるのは変わりませんでした...
結果的にリポジトリのコミット履歴は下の図のような状態で表せて、2回目のBのコミットは確かにスキップされています。
回避策
当たり前のことかもしれませんが、今回の事象を回避する方法を最後に記載します。筆者が実際に試したわけではないので、もし適用する際はご自身で挙動をお確かめください。
- コミットBをrevertするコミット
rBを最初から作らない -
rBを既に作ってしまった場合-
branch1に再度コミットBをcherry-pickする -
B(およびrB)を抜いたブランチを再度、mainブランチから分岐して作り直す
-
- 既に
branch1をmainブランチにマージしてしまった場合-
mainブランチにBをcherry-pickしてコミットする(もしくは同目的のブランチをmainブランチから新たに作る)
-
おわりに
「git rebaseは注意が必要」と言葉では聞いていましたが、今回初めて実感しました。