【Git】pushしようとしたら”non-fast-forward”で拒否されたときの解決方法

【Git】pushしようとしたら”non-fast-forward”で拒否されたときの解決方法 Git

Gitでgit pushを実行した際にnon-fast-forwardエラーで拒否されるのは、
リモートの履歴が自分のローカルよりも先に進んでいる、あるいは互いに分岐していて「早送り(fast-forward)」で反映できないためです。
解決には、まず最新の履歴を取り込み、ローカルの履歴を整えてから改めてpushする必要があります。

non-fast-forwardとは何か

fast-forwardは、リモート先端がローカルの先祖に当たるときに、単にポインタを進めるだけで履歴が一致する状態です。
一方で、リモート側に自分の知らないコミットが存在する、またはローカルとリモートが別々に進んで分岐している場合は、
fast-forwardでは整合できず、non-fast-forwardとしてpushが拒否されます。

まずやるべき状況確認

先祖関係と差分を把握してから統合方法を選びます。fetchで最新を取得し、ログで分岐状況を確認します。

# 最新の参照を取得して不要な参照を掃除
git fetch --prune

# 現在地を確認
git status

# 分岐状況を可視化
git log --oneline --decorate --graph --all -n 30

ケースA:自分にローカルコミットがなく、リモートが先行している

ローカルに未コミットの変更や新規コミットがないなら、早送りで取り込んでからpushできます。

# 例:mainブランチを早送り
git fetch --prune
git merge --ff-only origin/main
git push origin main

pullを使う場合も、余計なマージコミットを避けるために--ff-onlyを指定すると履歴がきれいに保てます。

git pull --ff-only
git push

ケースB:自分もリモートも新しいコミットがあり、互いに分岐している

分岐を解消して一本化してからpushします。履歴の見通しを重視するならリベース、統合点を明示したいならマージを選びます。

# リベースで直線化する例
git fetch --prune
git rebase origin/main
# コンフリクトが出たら解消→ステージ→続行
git add .
git rebase --continue
# 完了後にpush(必要なら upstream を明示)
git push origin HEAD

マージで取り込む場合は、マージコミットが一つ追加されます。

# マージで取り込む例
git fetch --prune
git merge origin/main
# コンフリクト時は解消→ステージ→コミット
git add .
git commit
git push

ケースC:直前に誰かがさらに更新していて、整えた後でもpushが拒否される

統合後のpushが再び拒否される場合は、その間にリモートが進んだ可能性があります。再度fetchして差分を解消します。
公開済みブランチで自分が履歴書き換え(リベースなど)を行った場合は、チーム合意のうえで--force-with-leaseが必要になることがあります。

# 最新を取得して再調整
git fetch --prune
git rebase origin/main    # または git merge origin/main

# 履歴書き換え後の最終手段(他者の更新を保護)
git push --force-with-lease

ケースD:保護ブランチやフックによりpushが拒否されている

リポジトリ設定で保護されているブランチは、リベース後の強制pushが禁止されている場合があります。
CI必須やレビュー必須のルール、サーバー側フックの制約で拒否されることもあるため、エラーメッセージを確認し、必要ならPR経由で統合します。

ケースE:追跡ブランチの設定やリモート指定が誤っている

別名のブランチにpushしようとしている、upstreamが未設定などの単純な設定ミスでも発生します。
追跡先を設定し直すと解消します。

# 現在のupstreamを確認
git rev-parse --abbrev-ref --symbolic-full-name @{u}

# 追跡設定(現在のブランチを origin/main にひも付け)
git branch --set-upstream-to=origin/main

# 明示的に push 先を指定
git push origin HEAD

コンフリクト発生時の進め方

コンフリクトが出たら、表示箇所を修正し、ステージして続行します。難しければ中断してやり直せます。

# 解消して続行(リベース中)
git add .
git rebase --continue

# 中断して開始前へ戻す
git rebase --abort
git merge --abort

再発防止の設定と運用

こまめにfetchし、pull戦略をチーム方針に合わせて統一すると衝突が減ります。
参照掃除を有効化し、未コミット変更の自動退避を設定しておくと日常運用が安定します。

# fetchで不要参照を自動削除
git config --global fetch.prune true

# pullをデフォルトでリベース(直線履歴を採用する場合)
git config --global pull.rebase true

# リベース時に未ステージ変更を自動退避
git config --global rebase.autoStash true

まとめ

non-fast-forwardは、リモートの履歴が先行しているか分岐しているサインです。
まずgit fetchで最新を取得し、リベースかマージで統合してからpushすれば解決します。
どうしても履歴書き換えが必要な場合は--force-with-leaseとチーム合意を徹底し、日頃からpull戦略と参照掃除の設定を整えて再発を防ぎましょう。