Git pushのnon-fast-forward拒否を安全に解消する|履歴を統合してから再送

Git pushのnon-fast-forward拒否を、push先・履歴差・既存作業から診断し、履歴を失わない統合と中断・再確認へ進めます。

AIものさし編集部

Git pushがnon-fast-forwardで拒否されたら、force pushではなく、送信先branchとローカル・remoteの履歴差を確認します。remoteに自分が持っていないcommitがある場合も、自分がamendやrebaseで履歴を書き換えた場合もあるため、同じ手順で上書きしません。

この記事は、自分の作業branchの変更を既存履歴と統合するための診断手順です。以下のoriginとwork-demoは架空のremote・branch名です。実repositoryでfetch、merge、rebase、commit、push、stash操作は行っていません。コマンドは条件を確認してから使う説明例です。

未保存作業と送信先を先に確認する

次は状態を読むための例です。branch名、追跡先、作業中の変更を確認します。remote URLには認証情報や内部情報が含まれる場合があるため、共有前に必要な範囲へ絞ります。

git status --short --branch
git branch -vv
git remote -v

表は横にスクロールできます

確認項目調べること次へ進まない条件
現在branch今送ろうとしている作業か別branch・detached HEADで意図不明
push先remoteと送信先branch誤ったforkや共有branch
WIP未commit・未追跡の必要ファイル保全方法が決まっていない
操作中状態merge/rebaseが進行中か前の操作を放置したまま

必要なWIPがある場合は、projectの手順で保存してから統合します。無条件のgit add .、reset、stashの一括操作を前提にしません。Gitのmerge資料(新しいタブで開く)は、未commit変更がある状態でのmergeでは、中断しても元の変更を再構築できない場合があると注意しています。

remote情報を更新して履歴を比較する

fetchはremote-tracking情報を更新し、mergeやrebaseとは別の操作です。送信先を確認したうえで取得し、差分を読んでから統合方法を選びます。次はorigin/work-demoへ送る架空例です。

git fetch origin
git log --oneline --left-right HEAD...origin/work-demo

この比較では左がHEAD側だけ、右がremote-tracking側だけにあるcommitです。差分がないことを確認するには、正しい送信先の最新情報をfetchできたことも必要です。認証やnetworkの失敗があれば、古いremote-tracking情報を最新状態とは扱いません。

表は横にスクロールできます

履歴の状態意味選ぶ方向
remote側だけ進んでいるローカルがbehindfast-forwardで追いつけるか確認
両側に固有commitがある履歴が分岐チーム方針に沿うmerge/rebase
ローカルだけ進んでいる通常は送信可能な関係push先や最新取得、別の拒否理由を再確認
remoteの対応関係が不明比較対象が違う可能性統合より先にbranch/remote確認

behindだけならfast-forwardを確認する

WIPを保全し、対象branchが正しい場合、次はfast-forwardできるときだけ進める例です。

git merge --ff-only origin/work-demo

分岐していれば、この条件では統合できません。その失敗をforceで押し切らず、両側の固有commitを読みます。fast-forwardが通った後も、自分が想定した履歴になったか、作業内容が揃ったかを確認します。

分岐している場合は履歴を残す方針を選ぶ

GitHubのnon-fast-forward説明(新しいタブで開く)は、remoteの変更を統合してから再送する流れを案内しています。mergeは両側の履歴を統合し、rebaseはローカルcommitを別の基点へ載せ直します。すでに共有したcommitをrebaseすると他の利用者へ影響するため、単に履歴を見やすくする目的で選びません。

表は横にスクロールできます

方針確認する前提中断する場合
mergeprojectがmerge commitを許容するか進行中のmergeならmerge --abort
rebase載せ直すcommitの共有状況・チーム方針進行中のrebaseならrebase --abort
統合せず確認意図しないremote変更・rewrite所有者と履歴の意図を確認

mergeなら対象remote branchを指定して統合し、競合があれば該当ファイルの両側の意図を確認します。rebaseを選ぶ場合も、競合する各commitを解決して続行する手順になります。どちらも、意味の分からない変更を一括でours/theirsへ寄せて完成とはしません。

中断コマンドは、対象操作が進行中のときに使います。未保存作業を含むどんな状態でも完全に戻せる保証ではありません。統合前の作業保全と、開始時のcommit識別を記録しておくことが重要です。

再送前に検証し、再拒否なら再比較する

統合後は状態、履歴、対象差分、必要なprojectの検証を確認します。競合マーカーが消えたことだけでは、両側の機能が保たれた証明にはなりません。pushが許可された作業branchと送信先を再確認してから通常のpushを行う段階へ進みます。

その間に別の人がremoteを更新すれば、再び拒否される場合があります。前の診断を使い回してforce送信せず、改めてfetchと履歴比較を行います。branch保護や権限で拒否された場合はnon-fast-forwardとは別の理由として扱います。

--force-with-leaseも通常の履歴統合の代用ではありません。意図的な履歴書換えが必要なら、対象branch、共有者、復旧方法を決める別の作業です。今回の問題はまず、失われてはいけないremoteのcommitを含む履歴へ自分の変更を統合できるかで判断します。

確認した公式資料