Git 推送失敗的解決方案:non-fast-forward 問題指南

更新日期: 2024 年 12 月 22 日

本文為 Git 提交拆解案例探討,第 4 篇

  1. 如何將一個分支上的多項功能代碼分離到不同分支
  2. 用導航地圖比喻理解 Git 的 Commit 與 reset 操作邏輯
  3. 初學者指南:如何使用 git cherry-pick 挑選特定提交
  4. Git 推送失敗的解決方案:non-fast-forward 問題指南 👈 所在位置

當你嘗試推送到遠端分支時,可能會遇到 non-fast-forward 錯誤,Git 無法完成推送,並提示你先執行 git pull

這種情況通常是因為本地分支和遠端分支的提交歷史不一致。

本文將幫助你理解這個問題的成因,並提供多種解決方案,讓你能順利完成推送。


問題分析:什麼是 non-fast-forward

現象

執行 git push 時,出現以下錯誤:

error: failed to push some refs to '<remote-repo>'
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.

原因

  • 遠端分支有新的提交:你本地的分支沒有同步這些提交。
  • 歷史不一致:遠端分支包含的提交,本地分支並未包括,這導致 Git 無法自動執行快進(fast-forward)。

解決方案:三種同步策略

根據情況不同,可以選擇以下解決方法:

方案 1:合併遠端分支(適用於無衝突情況)

步驟

  1. 拉取遠端分支的最新提交並合併
git pull origin <branch-name>
  1. 解決衝突(如果有): 如果合併過程中出現衝突,解決衝突後,執行以下命令完成合併:
git add <resolved-files>
git commit
  1. 推送到遠端
git push origin <branch-name>

結果

  • 遠端的變更與本地的提交合併在一起。
  • 提交歷史保持完整,遠端和本地分支達成一致。

方案 2:重定基底(適用於需保留本地提交的情況)

步驟

  1. 執行重定基底: 將遠端的提交應用到本地分支之前,重新排列提交歷史:
git pull --rebase origin <branch-name>
  1. 解決衝突(如果有): 如果出現衝突,按照提示解決,然後繼續重定基底:
git add <resolved-files>
git rebase --continue
  1. 如果需要取消重定基底操作,可以執行:
git rebase --abort
  1. 強制推送到遠端: 由於重定基底改變了提交歷史,使用 --force-with-lease 來推送:
git push origin <branch-name> --force-with-lease

結果

  • 遠端提交排在本地提交之前,生成新的提交歷史。
  • 本地提交保留,遠端分支歷史得到同步。

方案 3:放棄本地變更,使用遠端最新狀態

步驟

  1. 重置本地分支到遠端分支的狀態: 如果本地的變更不重要,可以直接覆蓋本地分支:
 git reset --hard origin/<branch-name>
  1. 推送到遠端: 本地和遠端分支完全一致後,正常推送即可:
 git push origin <branch-name>

結果

  • 本地分支與遠端分支保持一致,所有本地未推送的提交會被刪除。

感謝你的指正!你說得對,視覺化圖片的錯誤在於遠端分支的進度應該超前於本地分支,這正是 non-fast-forward 錯誤的常見情況:遠端有新的提交,而本地尚未同步。讓我們修正這個問題,重新繪製正確的圖片和示意流程。


視覺化解釋

初始狀態

non-fast-forward 問題中,遠端分支比本地分支有更多的提交

假設遠端分支有提交 DE,但本地分支停留在 C 並進行了自己的開發,新增了提交 FG

遠端分支(origin/<branch-name>):
A---B---C---D---E

本地分支(<branch-name>):
A---B---C---F---G

這種情況下,Git 無法直接推送,因為本地分支的歷史不是遠端分支歷史的直接後續。


合併後的結果(方案 1:合併遠端分支)

通過 git pull 合併遠端的最新提交,結果如下:

遠端分支(origin/<branch-name>):
A---B---C---D---E---H
             \   /
              F---G

在此過程中:

  • 遠端分支的提交 DE 被合併進本地。
  • 本地新增了一個合併提交 H

重定基底後的結果(方案 2:重定基底)

通過 git pull --rebase 重定基底,結果如下:

遠端分支(origin/<branch-name>):
A---B---C---D---E---F'---G'

在此過程中:

  • 本地的提交 FG 被重新應用在遠端的最新提交之後,生成新的提交 F'G'
  • 提交歷史變得線性化。

重置後的結果(方案 3:放棄本地變更)

如果放棄本地變更,直接重置本地分支到遠端分支的狀態,結果如下:

遠端分支(origin/<branch-name>):
A---B---C---D---E

在此過程中:

  • 本地的提交 FG 被刪除。
  • 本地分支與遠端分支完全同步。

如何避免每次被詢問拉取方式?

可以通過配置 Git 的默認行為,簡化操作:

配置選項

  1. 默認合併(merge): 拉取時自動合併遠端提交:
 git config --global pull.rebase false
  1. 默認重定基底(rebase): 拉取時自動重定基底:
 git config --global pull.rebase true
  1. 僅允許快進(fast-forward): 拉取時僅允許快進模式:
 git config --global pull.ff only

簡單總結

  1. 確認差異:執行以下命令檢查本地和遠端的差異:
git fetch origin
git log --oneline origin/<branch-name>..<branch-name>
  1. 選擇同步策略
    • 合併(git pull):適用於需要合併變更的情況。
    • 重定基底(git pull --rebase):適用於保留本地提交的情況。
    • 重置(git reset --hard):適用於放棄本地變更的情況。
  2. 設置默認行為:使用 git config 設置默認的拉取策略,減少每次操作的選擇困擾。

通過這篇指南,希望你能輕鬆解決 non-fast-forward 錯誤,順利完成推送操作。

Similar Posts