何が起きたのか
git push を実行したら、こんなエラーが出た:
$ git push origin main
To https://github.com/your-org/your-repo.git
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to 'https://github.com/your-org/your-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.
ローカルブランチがリモートより遅れている状態だ。最後に pull した後、チームメンバーが新しいコミットをプッシュしており、Git は古い履歴の上に上書きプッシュさせないようにしている。
Git はリモートの新しいコミットが失われないよう保護している。ファイルの競合と同じ考え方で、コミットレベルで起きているだけだ。
なぜこうなるのか
原因となる流れはこうだ:
- コミット
Aの時点でリポジトリをクローンまたは pull した。 - チームメンバーがリモートにコミット
Bをプッシュした。 - 自分はローカルで独自のコミット
Cを作った — こちらもAを起点にしている。 Cをプッシュしようとするが、リモートはすでにBの状態にある。プッシュするとBが完全に消えてしまう。
Git はこれを non-fast-forward な状況と呼ぶ。ブランチを直線的に前進させることができず、他の人の作業を飛び越えてしまうことになる。
手っ取り早い修正:pull してマージする
共有ブランチでの日常的な作業では、まず pull してから Git にマージさせるのが基本だ:
# ステップ1:リモートから最新の変更を pull する
git pull origin main
# ステップ2:マージコンフリクトがある場合は解消する
# (競合ファイルを編集してから:)
git add .
git commit -m "Merge remote changes"
# ステップ3:再度プッシュする
git push origin main
Git はマージコミットのメッセージを求めるか、自動生成する。マージが成功すれば、問題なくプッシュできる。
別の修正方法:rebase で pull する(履歴をきれいに保つ)
マージコミットのない直線的な履歴が好みなら、--rebase を使おう:
# リモートの上にローカルコミットをリベースしながら pull する
git pull --rebase origin main
# リベースコンフリクトがある場合:
# 1. 競合ファイルを修正する
# 2. ステージングする
git add
# 3. リベースを続ける
git rebase --continue
# リベース完了後にプッシュする
git push origin main
rebase はリモートのコミットの上に自分のコミットを再適用する。結果として、リモートのコミットの後に変更を加えたかのように見える — 履歴が分岐しない。
rebase をデフォルトの pull 戦略にする
すべての git pull でマージの代わりに rebase するようにするには、一度だけ以下を実行する:
git config --global pull.rebase true
ブランチで一人作業している場合
別のマシンからプッシュして、手元のローカルコピーが古くなっているだけの場合もある。普通に pull すれば解決する:
git pull origin main
git push origin main
ローカルのコミットが不要な下書きの場合 — 例えば別のマシンから正式版を既にプッシュ済みなら — ローカルを消してリモートにリセットできる:
# ローカルブランチをリモートと完全に一致させる
git fetch origin
git reset --hard origin/main
注意:--hard はローカルの未コミット変更と、リモートにまだないコミットをすべて完全に削除する。ローカルの作業が不要だと確信している場合にのみ使用すること。
強制プッシュ — いつ・どうやって使うか(慎重に)
強制プッシュには正当な用途がある:PR 前に乱雑なコミット履歴を整理する場合や、自分だけが触っているフィーチャーブランチで rebase 後にプッシュする場合などだ。
# 強制プッシュ(リモートの履歴を書き換える)
git push --force origin feature/my-branch
より安全な選択肢は --force-with-lease だ。最後の fetch 以降に他の人がプッシュしていた場合はプッシュを拒否する — 便利な安全装置だ:
git push --force-with-lease origin feature/my-branch
main、master、その他の共有ブランチには絶対に強制プッシュしないこと(チーム全員が把握している場合を除く)。強制プッシュは履歴を書き換える。そのコミットを基に作業していた人は深刻な divergence 問題に直面し、一度実行すると簡単には元に戻せない。
修正できたか確認する
プッシュが成功したら、ローカルとリモートが同期していることを確認しよう:
# リモートトラッキングの状態を確認
git status
# 期待される出力:"Your branch is up to date with 'origin/main'."
# リモートを含む最近のコミットを表示
git log --oneline -10
# ローカルとリモートを直接比較
git fetch origin
git log HEAD..origin/main --oneline
# 出力なし = 完全に同期済み
チェックリスト:どの修正方法を使うか
- 共有ブランチ(main、develop) →
git pullまたはgit pull --rebase。強制プッシュは絶対禁止。 - 自分だけのフィーチャーブランチ → rebase 後の強制プッシュは許容範囲。
- ローカルのコミットが不要 →
git fetch+git reset --hard origin/main。 - きれいな直線履歴が欲しい →
git pull --rebase。
再発防止策
作業を始める前に毎回 pull しよう。5秒で済み、このような状況をまるごと回避できる:
# 毎回のセッションをこれで始める:
git pull origin main
# またはマージせずに変更内容だけ確認したい場合:
git fetch origin
git log HEAD..origin/main --oneline # リモートの新着を確認
フィーチャーブランチを使うチームでは、main での競合はほとんど起きない。本当の危険は、同じフィーチャーブランチを二人が連絡なしに作業している場合だ。ブランチの寿命を短くし、プッシュ前にコミュニケーションを取れば、このエラーは滅多に起きなくなる。

