◀ 4.

git denying non-fast-forward refs/heads/master の原因と対処(pull

▶

non-fast-forward拒否はforce要求ではありません。履歴の分岐と受信側の拒否を分け、保護を維持して取込み方を選びます。

診断、保存、比較、操作の選択、結果確認の順序

この記事の要点
  • git push 時に 「denying non-fast-forward refs/heads/master (you should pull first)」 が表示される
  • 原因: リモートにローカルに無いコミットがある、または git rebase / git commit --amend でローカルが履歴書き換えしている
  • 最初に送り先・対象ref・分岐と拒否の種類を確認。取込み方式はmerge / rebase / ff-onlyを選ぶ
  • force-with-leaseは期待OIDの照合であり、内容・権限・保護・復旧を保証しない
  • GitHub / GitLab の保護ブランチ設定で拒否されている場合は、許可された別branchと PR / MR 等のレビュー経路を使う。保護を迂回しない

このエラーの概要

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 という意味です。更新前のリモート先端が送信するcommitの祖先でない更新です。分岐・巻戻し・別履歴・送り先間違いなどを実際のOIDで調べます。エラーだけでは誰の操作が原因か確定しません。

原因の典型

状況意味対処
他の人が先に push したリモートが進んでいる未共有commitならrebase、共有済みならmerge等を検討。確認後に通常push
git commit --amend 後 push旧commitとの関係を確認未公開部分のみなら通常push候補。公開済みなら新branch等も検討
git rebase 後 push送信先が祖先か確認祖先なら通常push候補。公開履歴変更は別途合意と保護確認
git reset --hard で過去戻し作業損失と履歴の巻戻しに注意診断として実行しない。共有変更の取消しはrevert等を検討
保護ブランチで拒否GitHub/GitLab 設定許可されたPR / MR経路でレビュー

対処 1: 状況に応じて取込み方式を選び、再 push

他の人が先に push しただけなら、リモート最新を取り込んで自分のコミットを上に積み直します:

旧例の訂正・実行前の確認:このpullは現在branchへorigin/masterを取り込む例で、必ずmasterへ切り替わるわけではありません。先にstatus・branch・push URL・refspecを確認します。未共有の独自commitを積み直すときだけrebaseを選び、共有済みならmerge等を相談します。

# リモートの変更を取り込む(rebase で履歴をきれいに)
git pull --rebase origin master

# コンフリクトが出たら解決
# ファイルを編集 → git add → git rebase --continue

# 再 push
git push origin master

設定任せのgit pullはrebaseやff-onlyにもなり得ます。mergeを選ぶならgit pull --no-rebase等で方式を明示します。fast-forwardならmerge commitは作られず、分岐時も競合・依存・テストの確認が必要です。

対処 2: --force-with-lease の条件と限界

rebase / amend後でも強制pushが必須とは限りません。未公開commitだけを積み直して送信先の先端を祖先に含めれば通常pushできます。公開履歴を置き換える必要が本当にあるか、履歴を保ったmerge・revertや新しいbranchのレビューで済むかを先に判断します。

旧例の訂正・実行前の確認:以下の「推奨」「stale後に再度force」は旧コメントです。そのまま実行しません。stale時は停止して新しい先端と必要な変更を調べ、fetch→再forceを定型手順にしません。leaseも保護 / receive.denyNonFastForwards / hookを解除せず、--forceとの併用は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

省略形のleaseは通常remote-tracking参照を期待値にします。手動やbackground fetchがその参照を進めると、未確認の他者commitを上書きする予定まで通り得ます。明示形 --force-with-lease=refs/heads/master:期待OID は比較する値を固定できますが、そのOIDの内容を捨ててよいかは人のレビューと合意が必要です。

対処 3: なぜ履歴書き換えになったか確認

旧例の訂正・実行前の確認:origin/masterはローカルの追跡参照で、常に現在のサーバー先端ではありません。取得refspecとpush先が一致するか確認し、fetchは追跡参照等を書き換える操作として別途実施します。reflogの日時や番号だけで復旧先を決めません。

# ローカルとリモートのログを比較
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 を作成、レビュー後マージ
  • 保護で拒否されたら管理者にルールと許可されたレビュー経路を確認する。エラー解消のため保護を弱めない
  • 緊急時も承認済みの事故対応手順で保全・影響評価を行う。サーバー直接resetで迂回しない

force を検討する前の判断基準

シナリオ推奨手段
個人ブランチで自分しか触らない利用者・CI・PR等の参照と規則を確認。個人名だけで許可しない
feature ブランチを複数人で共有周知だけでなく関係者の合意・退避・許可された経路が必要
master / main / develop通常のレビュー / merge / revertを優先。通知だけで変更許可にならない
公開 OSS の main公開利用者への影響が大きい。運用規則に従い履歴を保つ案を優先

force push の事故から復旧

復旧可能性は旧OIDの特定とオブジェクトの実在・到達可能性・保持期限に依存します。reflogは各ローカルの参照移動記録で、全員分やサーバー側に必ず存在するバックアップではありません。OIDだけ分かってもオブジェクトが失われていれば復元できません。活動ログから取得できる保証もありません。まず追加更新を止めて関係者と保全し、見つかった旧commitは新しい退避branch等に確保して内容を比較します:

旧例の訂正・実行前の確認:このreset --hardと再forceは復旧の標準手順ではなく、実行しません。hard resetは現在の作業を失い得て、再forceは事故後に追加された変更を再び除外し得ます。旧commitの実在と内容を確認し、別branchでレビューして必要な変更を通常の手続で反映します。

# 元のコミットを持っている人がいたら、その人のローカルから 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でも欠落や競合は起きます。進行中のrebaseを取りやめるなら--abortですが、途中に追加した作業まで完全復元する保証ではありません。

Q: --force-with-lease でも拒否される
A: stale infoは期待OIDの不一致を示し、権限・hook・保護等の拒否とは区別します。停止して相手先・期待値・実際の先端と差分を比較し、通常の取込み・新branch・管理者への確認を選びます。fetchしてleaseを更新しただけで再forceしません。

Q: 履歴を書き換えずに push したい
A: mergeは既存commitの親関係を保って統合する案ですが、競合解決で必要な変更を落とさない確認が必要です。独立した履歴や送り先間違いを通常mergeで解決できるとは限りません。

拒否を読み分けてから操作する

旧版にあった--sharedの説明は、git init --sharedが既定でreceive.denyNonFastForwardsを有効にするという条件です。bare作成一般や全サーバーに同じ設定があるという意味ではありません。この設定はforce付きの非fast-forwardも拒否します。旧版のサーバー側git reset --softという案は受信側の検査を迂回するため採用しません。管理者には設定 / hook / 権限を読取確認してもらい、保護を維持した解決経路を相談します。

送信先の先端が自分の祖先なら通常push候補、自分が遅れているだけならff-only候補、両側に独自commitがあればmergeまたは未共有部分のrebaseを検討します。秘密値の取消しならrevertだけで漏えいは解消しないため、資格情報の失効等も別途必要です。

通信しない確認例

Bashでrepoのトップから実行します。origin/masterは確認対象の例で、自動取得しません。URLやdiffには秘密情報が含まれ得るので出力を公開しないでください。引数が不正・参照が未取得なら停止します。

#!/usr/bin/env bash
set -eu
export GIT_OPTIONAL_LOCKS=0
if [ "$#" -ne 1 ] || [ ! -e .git ]; then
    printf '%s\n' 'Run at a worktree root: bash inspect-rejection.sh origin/master' >&2
    exit 2
fi
case "$1" in
    refs/remotes/origin/*|origin/*) ;;
    *) printf '%s\n' 'Pass a local remote-tracking ref, not a URL or option.' >&2; exit 2 ;;
esac
git rev-parse --verify "$1^{commit}" >/dev/null
git status --short --branch
git branch --show-current
git remote get-url --push --all origin
git config --show-origin --get-all remote.origin.fetch || test "$?" -eq 1
git rev-list --left-right --count "HEAD...$1"
git log --oneline --left-right --max-count=20 "HEAD...$1"
# No fetch, merge, rebase, push, ref update, or configuration write.

左右の件数は左がHEADだけ、右が指定参照だけにあるcommit数です。0/0でも作業ツリーやサーバー現在値の一致までは意味しません。比較の後に取込み方式を決め、通常pushが可能かを先に確認します。

明示leaseとdry-runの位置付け

合意された履歴変更を検討する場合に限り、確認済みの完全OIDを期待値として --force-with-lease=refs/heads/master:期待OID と明示refspec HEAD:refs/heads/master を指定し、まず --dry-run で予定を確認します。これはそのまま貼り付ける完成コマンドではありません。期待値を更新するたびに差分と合意を再確認します。--force-if-includesは省略形lease等の補助検査ですが、leaseなしや明示期待OID付きではno-opです。dry-runでは実更新しないので、実際の受信hook・保護・同時更新時の本実行成功や内容の安全を保証しません。

公式資料と検証範囲

2026年10月26日確認:pushのlease / dry-run、init --shared、receive.denyNonFastForwards、stashの保存 / pop / apply、rebaseの中断 / autostash、checkoutのindex参照、resetの範囲、cleanの予定表示、reflogのローカル記録。旧コードは比較資料として保持し、誤コメントは直前で訂正しています。追加例と合成履歴は隔離したローカルrepoで検証し、強制更新はdry-runのみで全ref不変を検査します。実サービスの保護 / hook / 権限、本番復旧、実clean / hard reset、全submodule構成を検証するものではありません。

子ページ

子ページはありません

同階層のページ
  1. fatal: remote origin already exists.
  2. fatal: '~' does not appear to be a git repository
  3. Cannot rebase: You have unstaged changes. Please commit or stash them.
  4. remote: error: denying non-fast-forward refs/heads/master (you should pull first)
  5. error: pathspec ... did not match any file(s) known to git.
  6. The following untracked working tree files would be overwritten by checkout
  7. fatal: Not a valid object name: 'master'.
  8. Unlink of file 'ファイル名' failed. Should I try again? (y/n)
  9. Another git process seems to be running in this repository
  10. error: Your local changes to the following files would be overwritten by checkout:

最近更新/作成されたページ