タイトル: remote: error: denying non-fast-forward refs/heads/master (you should pull first)
SEOタイトル: git denying non-fast-forward refs/heads/master の原因と対処(pull
| この記事の要点 |
|
このエラーの概要
git push 実行時に次のような拒否メッセージが返ってきます:
$ git push origin master
To gitserver:repo.git
! [remote rejected] master -> master (denying non-fast-forward refs/heads/master (you should pull first))
error: failed to push some refs to 'gitserver:repo.git'
# GitHub の場合の別パターン
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'github.com:user/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
「fast-forward でない」とは、リモートの履歴を直線的に進める形ではない push という意味です。リモートに自分が持っていないコミットがある(誰かが先に push した)、または自分が amend / rebase でローカルを書き換えた、のどちらかです。
原因の典型
| 状況 | 意味 | 対処 |
|---|---|---|
| 他の人が先に push した | リモートが進んでいる | git pull --rebase + push |
git commit --amend 後 push | 履歴書き換え | --force-with-lease |
git rebase 後 push | 履歴書き換え | --force-with-lease |
git reset --hard で過去戻し | 履歴書き換え | --force-with-lease |
| 保護ブランチで拒否 | GitHub/GitLab 設定 | PR 経由 / 保護ルール変更 |
対処 1: pull --rebase で取り込み再 push(通常はこれ)
他の人が先に push しただけなら、リモート最新を取り込んで自分のコミットを上に積み直します:
# リモートの変更を取り込む(rebase で履歴をきれいに)
git pull --rebase origin master
# コンフリクトが出たら解決
# ファイルを編集 → git add → git rebase --continue
# 再 push
git push origin master
merge コミットを許容するなら git pull でも可(merge コミットができます)。
対処 2: --force-with-lease で安全に強制 push
自分が rebase / amend で履歴を書き換えた場合は強制 push が必要です。--force は他人の作業を吹き飛ばす危険があるため、--force-with-lease を使います:
# 推奨: --force-with-lease
# リモートが自分が認識しているコミットから動いていない時のみ push 成功
git push --force-with-lease origin master
# 失敗した場合(他人が push 済 = 上書きすると消える)
# ! [rejected] master -> master (stale info)
# このときは pull --rebase で取り込んでから再度 force-with-lease
# 非推奨: 完全強制(他人の push があっても消す)
git push --force origin master
--force-with-lease は「リモートが自分が最後に見た状態と同じなら上書き」というロジックで、他人の push を踏み潰すリスクを下げます。
対処 3: なぜ履歴書き換えになったか確認
# ローカルとリモートのログを比較
git log --oneline origin/master..master # ローカルにあってリモートに無い
git log --oneline master..origin/master # リモートにあってローカルに無い
# 直近の reflog(操作履歴)
git reflog -10
# amend / rebase 後はコミット SHA が変わる
git log --oneline -5
対処 4: 保護ブランチで拒否されている場合
GitHub / GitLab / Bitbucket では master / main を保護ブランチに指定し、直接 push を禁止できます。この場合のメッセージは少し違います:
remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: At least 1 approving review is required by reviewers with write access.
対処は運用方針による:
- 正攻法: 別ブランチに push し Pull Request を作成、レビュー後マージ
- 保護ルール変更: GitHub → Settings → Branches → Branch protection rules を編集(管理者権限必須)
- 緊急対応: 保護ルールを一時無効化(記録に残る、推奨されない)
force 推奨ガイドライン
| シナリオ | 推奨手段 |
|---|---|
| 個人ブランチで自分しか触らない | --force-with-lease OK |
| feature ブランチを複数人で共有 | 事前にチームへ周知 → --force-with-lease |
| master / main / develop | 原則禁止。どうしてもなら全員に通知 |
| 公開 OSS の main | 絶対禁止(コミット ID で参照する人がいる) |
force push の事故から復旧
強制 push で他人のコミットを上書きしてしまった場合、リモートには履歴が残らなくても誰かのローカルか reflogに残っていれば復旧できます:
# 元のコミットを持っている人がいたら、その人のローカルから push
# または GitHub の場合 Events / Activity から SHA を発掘可能
# reflog から
git reflog
# abc123 HEAD@{2}: commit: lost commit
git reset --hard abc123
git push --force-with-lease origin master
FAQ
Q: pull --rebase でコンフリクトが大量に出る
A: 「ローカル変更が少ない」「リモート変更が少ない」どちらが少ない側に揃えるか判断。少ない方をcherry-pick し直す方が早いことも。git rebase --abort でやり直し可能。
Q: --force-with-lease でも拒否される
A: 自分が最後に fetch してから他人が push した状態。git fetch で最新を取得して再判断してから force するか、pull --rebase で取り込んでください。
Q: 履歴を書き換えずに push したい
A: git pull --no-rebase(merge 戦略)で取り込めば履歴は分岐するが上書きにはなりません。merge コミット 1 つで両方の作業を保持できます。