はじめに
チーム開発中に git pull したら急にこんなエラーが出た。
fatal: Need to specify how to reconcile divergent branches.
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint: git config pull.rebase false # merge
hint: git config pull.rebase true # rebase
hint: git config pull.ff only # fast-forward only
hint:
hint: You can replace "git config" with "git config --global" to set a default
hint: preference for all repositories. You can also pass --rebase, --no-rebase,
hint: or --ff-only on the command line to override the configured default per
hint: invocation.
調べたことをまとめておく。
何が起きていたか
ローカルとリモートの両方に、お互いが知らないコミットがある状態を「divergent(分岐した)」と呼ぶらしい。
リモート: A - B - C
\
ローカル: A - B - D
この状態で git pull すると、Git が「どうやって統合すればいい?」と判断できなくなる。昔のバージョンはデフォルトでマージを選んでくれていたが、最近のバージョンはユーザーに選択を委ねるようになっている。
選択肢は3つ
Git がヒントで教えてくれている通り、統合方法は3択。
1. マージ(--no-rebase)
git pull origin dev --no-rebase
リモートの変更をマージコミットとして取り込む。履歴がそのまま残るので、チーム開発での git pull はこれが無難だと思う。
A - B - C - M ← マージコミット
\ /
D
2. リベース(--rebase)
git pull origin dev --rebase
自分のコミット(D)をリモートの最新(C)の後ろに付け替える。履歴が一直線になって見やすいが、コンフリクトが起きたときの解消がマージより少し複雑になる。
A - B - C - D' ← D を付け替えた
3. Fast-forward のみ(--ff-only)
git pull origin dev --ff-only
ローカルに独自のコミットがない場合だけ成功する。divergent な状態では fatal になるので、今回は使えなかった。
デフォルト動作を設定しておく
毎回オプションを書くのが面倒なので、グローバル設定で決めておくのが楽。
# マージをデフォルトにする場合
git config --global pull.rebase false
# リベースをデフォルトにする場合
git config --global pull.rebase true
チーム開発では --no-rebase(マージ)をデフォルトにしておくのが安全だと思う。リベースは履歴を書き換えるので、push 済みのコミットに使うとチームメンバーを困らせることがある。
今回の対処
--no-rebase で pull した。
git pull origin dev --no-rebase
これでリモートの dev ブランチの変更がマージコミットとして取り込まれる。コンフリクトが出たら解消して、作業を続けるだけ。
まとめ
| 方法 | コマンド | 特徴 |
|---|---|---|
| マージ | --no-rebase |
履歴が残る。チーム開発で安定 |
| リベース | --rebase |
履歴が一直線。push 済みには注意 |
| FF のみ | --ff-only |
divergent では使えない |
「なぜ分岐したのか」を意識して作業するようにしたら、Git の動きがだいぶ読めるようになってきた気がする。