Dependabotが同じタイミングで複数のPRを出してきて、package.jsonとpnpm-lock.yamlを触るPRを先にマージしたら、残りのPRが軒並みコンフリクトする状態になった。これはDependabot特有の話ではなく、複数人が同じファイルを触るブランチを並行して作っていれば普通に起きる状況なので、rebaseとmergeそれぞれでの解消手順と違いを整理しておく。
状況
- PR A と PR B は、同じコミットの
mainから分岐している - PR A が先にマージされ、
mainが更新される - PR B のブランチは古い
mainを土台にしたままなので、mainとマージしようとするとPR Aが変更した箇所とコンフリクトする
同じmainから分岐している、かつ、PR AとPR Bが同じファイルの同じ・あるいは近い行を書き換えている場合には、コンフリクトする。
今回は、両方のPRがpackage.jsonやpnpm-lock.yamlを編集したためにコンフリクトが発生した。
この状態のPR Bを解消する方法が、大きく分けてrebaseとmergeの2通りある。
rebaseで解消する
PR Bのブランチのコミットを、最新のmainの上に積み直す方法。
git fetch origin
git switch pr-b-branch
git rebase origin/main
コンフリクトが起きると、そのコミットの適用が止まる。
# コンフリクトしたファイルを手動で編集して解消する
git add <解消したファイル>
git rebase --continue
PR Bに複数コミットが積まれている場合、コミットごとにこの作業を繰り返すことになる。全部解消したら、履歴を書き換えているのでリモートへは force push が必要。
git push --force-with-lease
--forceではなく--force-with-leaseを使うと、自分がfetchした時点から他人がリモートを更新していた場合はpushが止まる。共有ブランチで他人の変更を上書きしてしまう事故を防げる。
やめたくなったらgit rebase --abortで、rebase開始前の状態に戻せる。
mergeで解消する
PR Bのブランチに、最新のmainを取り込む方法。
git switch pr-b-branch
git merge origin/main
コンフリクトが起きたら同様に手動で解消する。
git add <解消したファイル>
git commit
マージコミットができるだけで、既存のコミットは書き換えられない。なのでpushも通常通りでよく、force pushは不要。
git push
こちらもgit merge --abortで開始前の状態に戻せる。
比較
| rebase | merge | |
|---|---|---|
| 履歴の形 | 直線的になる(PR Bのコミットがmainの先頭に積み直される) | 分岐がそのまま残り、マージコミットができる |
| 既存コミットの扱い | 書き換わる(コミットハッシュが変わる) | 変わらない |
| push方法 | force push(--force-with-lease推奨) | 通常のpush |
| コンフリクト解消の回数 | コミットの数だけ発生しうる | 1回にまとめて解消 |
| ブランチを他人と共有している場合 | 危険(force pushで他人の履歴を壊しうる) | 安全 |
一番の判断基準は「そのブランチを自分以外も触っているか」だと思う。自分しかpushしていない作業ブランチなら、rebaseで履歴をきれいな直線に保てる。逆に他の人も同じブランチにpushしている、あるいはそのブランチから別のブランチを切っている人がいる場合は、force pushが履歴を壊す原因になるのでmergeの方が安全。
ちなみに、Dependabotが出すPRはbot側が自動でrebaseしてくれるため、待つだけで解消するので対応不要。