1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

git rebase --onto と reflogでsquash merge時のconflictに対応する

1
Last updated at Posted at 2025-11-27

概要

branch-aからブランチを切ったbranch-bで作業する。
branch-aをsquash mergeしたあとにbranch-bをmergeしようとするとコンフリクトすることがある。
この際の対処法は以下のコマンド。

git rebase --onto origin/main branch-a branch-b

branch-bからさらにブランチを切ったbranch-cでも作業している時はbranch-bで上記コマンドを実行してしまうと(すでにrebaseしてしまっているため)コミットIDがズレるので上記のままでは使えない
その際には以下のコマンドでrebase前のコミットIDを掴んでそのIDでrebase --ontoする

git reflog show branch-b

本記事ではこれ以上の説明はありません。
以下ではこれが必要な場面の再現と対処法をなるべく詳細に解説します。

こんな時の対処法です

a.txt, b.txt, c.txtがあります。

image.png

branch-a でa.txtに修正を加えます。

image.png

まだbranch-aはmainにマージされていませんがbranch-aの修正内容を反映したまま作業したかったのでbranch-bはbranch-aからブランチを切って作業しました。
その際にbranch-aで修正されたファイル(a.txt) を一部修正しました。

image.png

両方のプルリクを作成しました。mainブランチはスッキリさせたいのでsquash mergeしましょう。

image.png

ブランチAをsquash mergeしました。この調子でブランチBもmergeしちゃいましょう。

image.png

残念、コンフリクトしました。

image.png

なぜ起こるか?どうすればよい?

squash mergeするとそのPRで作られたcommit とは別に、そのPRすべての差分をまとめた新しいコミットがmainに作られます。
結果として、main 上には branch-aで作ったコミットそのもの は存在せず、branch-b の祖先にあるコミットと main 上の コミットは異なるコミットとして扱われます。
そのため、branch-b を main に統合する際、a.txt の 同じ箇所に対する変更 が、

  • branch-b 側: branch-aで作られたコミットを前提とした変更
  • main 側: branch-aで作られたコミットをまとめた別の変更

というように祖先関係の切れた別変更 としてぶつかり、どちらが偉いかが判別できなくなり、コンフリクトをするのです。

本来コンフリクトしていない(squash mergeを選ばなければconflictしない)のにコンフリクト解消を頑張るのはちょっと無駄です。
そこで、rebase --ontoを使います。

rebase --ontoとは? & 解決

ひとことで言うとブランチを切った場所を変更できるコマンドです。

今の状況を図解すると以下の感じです。

o───o───o    ← origin/main
         \          
          A1───A2───A3  ← branch-a
                      \
                       B1───B2  ← branch-b (HEAD)

squash mergeするとそのPRで作られたcommit とは別に、そのPRすべての差分をまとめた新しいコミットがmainに作られるのでした。
そのため、以下のような状態になります。
これで別々のコミットが同じものを編集していることになりますから、branch-bをマージしようとするとコンフリクトするのでした。

o───o───o───S    ← origin/main (S = squash(A1,A2,A3))
         \          
          A1───A2───A3  ← branch-a
                      \
                       B1───B2  ← branch-b (HEAD)

そこで、git rebase --ontoを使います。
このコマンドの使い方は

git rebase --onto <付け替え先のブランチ> <付け替え元のブランチ> <付け替えたいブランチ>

です。今回はbranch-bを切った元のブランチをbranch-a -> origin/mainだったことに変更したいので、以下のようにすればOKです。

git rebase --onto origin/main branch-a branch-b

これで、再度pushしましょう。コミットIDが変わっているのでforce-pushします。

無事mergeできるようになりました。

image.png

さらにbranch-cがあった場合

上記の話ではbranch-bまででしたが、branch-bからさらに切ったbranch-cもあった場合はどうすれば良いでしょうか?
先程までの話だと、

git rebase --onto origin/main branch-a branch-b

して、branch-bをrebaseしたあとに、(merge, git fetchして、)

git rebase --onto origin/main branch-b branch-c

をすれば良さそうな気がします。実際にやってみます。

image.png

無事コンフリクトしました。

なぜコンフリクトするか?

これは簡単です。
答えはbranch-bをすでにrebaseしてしまっているからです。
思い出してみるとrebase --ontoは ブランチを切った場所を変更できるコマンドでした。

しかし、branch-bはすでにrebaseしてしまったため、今branch-bが持っているコミットIDはもはやbranch-cで持っているものではありません。
そのため、どこから付け替えればいいかがわからずコンフリクトしてしまうというわけです。

どうすればよい?

git reflogです。
git reflogとは、Gitで操作履歴を見ることができるコマンドです。
これは余談ですがreflogを把握しておけば大抵のことはやらかしてOKだと思っています。

今は branch-bをrebaseしたことが原因でコンフリクトが起こっているのでした。
そこでrebaseする前のコミットログを掴んでそこを基準にしてrebaseすればうまくいきそうです。

git reflog show branch-b

image.png

branch-b@{0} でrebaseされていますね。ということで直前のbranch-b@{1} をもとにrebaseしましょう。

git rebase --onto origin/main branch-b@{1} branch-c

image.png

image.png

無事にrebaseできて、マージもできるようになりました。
めでたしめでたし。

まとめ

  • squash mergeのコンフリクト解消はrebase --ontoで一発
  • 複数チェーンさせているときはreflogでrebase前のコミット掴んでrebaseする
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?