整理這些技術筆記真的很花時間,如果你願意 關閉 Adblock 支持我,我會把這份感謝轉換成更多「踩坑轉避坑」的內容給你!ヽ(・∀・)ノ
取代 git push --force 更好的方式,git push --force-with-lease

前言
我們在開發上,經常會使用 git rebase 或 git commit --amend 來整理 commit 歷史,但這些操作會重寫已經推送過的 commit,導致本地的歷史與遠端不同,這時候普通的 git push 會被拒絕,必須使用強制推送。
所以這一篇來聊一下 git push --force-with-lease,這是一個更安全的強制推送方式,它會在推送前先確認遠端沒有新的變更,才允許你強制推送。
git push
我相信大家應該非常熟悉 git push 這個指令了,基本上就是把你當前的修改進度推到遠端的 Git 儲存庫上(GitHub、GitLab、Bitbucket 等等),這樣其他同事就可以看到你的修改了。
所以大多人對於這指令只認識這幾個用法:
git push:把當前分支的修改推到遠端。git push origin <branch>:把指定分支的修改推到遠端。git push -u origin <branch>:把指定分支的修改推到遠端,並且設定上游分支,以便於以後可以直接使用git push而不需要指定分支。git push --force:強制推送修改到遠端,會覆蓋掉遠端的修改(今天介紹的主角)。
但其實它有非常多的進階用法,例如:
git push --allorgit push --branches:把所有分支的修改推到遠端。git push --tags:把所有標籤推到遠端。
因此你可以這樣做:
1 | |
等等,這些都是一些比較進階的用法,但今天我們要聊的重點是 git push --force 和 git push --force-with-lease,對於其他有興趣可以直接觀看 Git 官方文件。
git push –force & git push –force-with-lease
這個大概是萬惡的用法,你可以輸入 git push -f、git push --force、git push origin +<branch> 等等這些指令來 強制 把你的當前修改進度推到遠端上,這樣就算遠端上有其他同事的修改了,你也會直接覆蓋掉他們的修改(簡單來講,就是讓你同事今天做白工)。
所以許多公司跟單位是直接禁止使用 git push --force 的。
但我們實際開發上不可能完全不使用 git push --force,因為有時候我們可能會需要重寫 commit 歷史(例如使用 git rebase),這時候就會需要使用 git push --force 來把修改推到遠端上。
所以 Git 官方就提供了一個更安全的方式,也就是 git push --force-with-lease。
它的運作機制是:在你強制推送之前,Git 會比對遠端分支目前指向的 commit 是否與你本地記錄的遠端追蹤分支(例如 origin/main)一致。如果一致,代表在你上次 git fetch 之後沒有其他人推過新的 commit,就允許強制推送;如果不一致,就代表有人在這段期間推了新的東西,Git 會拒絕你的推送,避免你覆蓋掉別人的工作。
因此當你使用 git push --force-with-lease 的時候,如果遠端上有其他同事在你之後推了新的 commit,你就會看到類似下面的錯誤訊息:
1 | |
這裡的 (stale info) 代表你本地記錄的遠端狀態已經過時了,這時候你就需要先 git fetch 拉取遠端的最新狀態,重新整理你的修改(例如重新 git rebase),最後再使用 git push --force-with-lease 來推送。
什麼時候需要強制推送?
你可能會想:「我只要 git push 被拒絕的時候,先 git pull 然後再 git push 就好了啊?那幹嘛用 --force?」
沒錯,如果你的 commit 歷史跟遠端只是單純的分叉(例如你跟同事同時在同一個分支上各自 commit),確實用 git pull + git push 就可以解決。
但當你使用了以下這些會改寫 commit 歷史的操作時,情況就不同了:
git rebase:變基,重新排列 commitgit commit --amend:修改最後一個 commitgit reset後重新 commit:重置後重寫歷史
這些操作會產生全新的 commit SHA,導致你本地的歷史跟遠端的歷史完全不同,不是單純的「落後」而是「分叉且無法合併」,所以必須強制推送。而這時候用 --force-with-lease 就比 --force 安全得多。
–force-with-lease 的已知陷阱
--force-with-lease 雖然比 --force 安全,但它並不是萬無一失的。
它的保護機制是比對你本地記錄的遠端追蹤分支(例如 origin/main),而不是即時去查詢遠端。所以如果你在強制推送之前做了 git fetch(或 git pull),你本地的遠端追蹤分支就會被更新到最新狀態,這時候 --force-with-lease 會認為「你已經知道遠端的最新狀態了」,就會允許強制推送——保護就失效了。
舉個例子:
1 | |
–force-if-includes
為了解決這個陷阱,Git 2.30 之後提供了 --force-if-includes,它會額外檢查:遠端追蹤分支的最新 commit 是否已經被整合到你的本地分支中。如果你只是 git fetch 但沒有實際把遠端的變更整合進來,推送就會被拒絕。
建議搭配使用:
1 | |
如果你覺得每次都要打這麼長太麻煩,可以設定 Git alias:
1 | |
之後就可以直接使用:
1 | |
總結
| 指令 | 行為 | 安全性 |
|---|---|---|
git push |
只允許快轉(fast-forward)更新 | 最安全 |
git push --force-with-lease |
比對本地遠端追蹤分支,一致才允許強制推送 | 安全 |
git push --force-with-lease --force-if-includes |
額外確認遠端變更已被整合到本地 | 更安全 |
git push --force |
無條件覆蓋遠端 | 危險 |
簡單來說:
- 永遠不要用
git push --force,除非你非常確定你在做什麼。 - 用
git push --force-with-lease取代--force,這是最基本的安全措施。 - 搭配
--force-if-includes可以獲得更完整的保護。
養成好習慣,保護自己也保護同事的心血。
整理這些技術筆記真的很花時間,如果你願意 關閉 Adblock 支持我,我會把這份感謝轉換成更多「踩坑轉避坑」的內容給你!ヽ(・∀・)ノ