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側だけ進んでいる | ローカルがbehind | fast-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すると他の利用者へ影響するため、単に履歴を見やすくする目的で選びません。
表は横にスクロールできます
| 方針 | 確認する前提 | 中断する場合 |
|---|---|---|
| merge | projectが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を含む履歴へ自分の変更を統合できるかで判断します。
