使用 Force if Includes 安全的強制推送
本文資訊整理自 When should I use "git push --force-if-includes",原文跟論文沒兩樣,有夠長...
TL;DR
--force-if-includes 應該如何使用?
--force-if-includes
一定要和 --force-with-lease
同時使用才會生效。如果 --force-with-lease=<refname>:<hash>
兩個參數都設定了,則 --force-if-includes
也會變成 no-op (無作用)。
# 使用語法
git push --force-if-includes --force-with-lease[=<refname>] [<repo> [<refspec>]]
一般的使用方式是 git push --force-if-includes --force-with-lease origin main
看不懂文檔用語?請見讀懂文檔
應該使用 --force-if-includes 嗎?
原文作者建議不要使用,原因是
- 他認為是否能在所有情況下成功攔截錯誤仍有待驗證。
- 他認為工作原理類似於
git rebase --fork-point
,而--fork-point
並非在所有情況下都能可靠運作。
下方指令是原文作者強制推送的工作流程,也就是手動檢查:
# 檢查
git fetch <remote> # get commit(s) from remote
git show <remote>/<name> # inspect their most recent commit
# to make sure it's what we think
# it is, so that we're sure of what
# we're tossing *from* their Git repo
# 手動解決遠端更新應該怎麼處理
# 強制推送,如果不處理遠端更新,就代表把剛才 fetch 的結果全部覆蓋
git push --force-with-lease <remote> <name>
原文作者的說明非常詳細,但是不推薦的原因沒有仔細解釋,筆者自己的看法是可以嘗試使用。
--force-if-includes 檢查了什麼?
這不是一個全新的邏輯或功能,而是延伸了 --force-with-lease
,額外檢查「遠端最新提交是否存在於本地目標分支的 reflog 紀錄中」,以確保被推送的分支確實整合過這個提交。
深入強制推送
--force-with-lease 原理
大家應該都聽過 --force-with-lease
是不允許覆蓋別人的提交,那你有想過他是怎麼避免的嗎?有沒有可能 --force-with-lease
也會意外覆蓋他人提交?
答案是肯定的,--force-with-lease
只檢查「遠端最新提交是否出現在本地儲存庫」,所以在 --force-with-lease
前使用 git fetch
就等同於強制覆蓋,因此需要更嚴格的檢查手段。
深入 --force-with-lease
我們先搞清楚 --force-with-lease
的全部設定,語法如下:
# 語法
git push --force-with-lease[=<refname>[:<expect-remote-hash>]] [<repo> [<refspec>…]]
完整的使用範例如下:
# 推送到名為 origin 的遠端,推送的項目是 main 分支,只推送到 <dst-hash> 後續不推送
# 檢查項目為 refs/heads/main 的 hash 是否等於 expect-remote-hash
git push --force-with-lease=refs/heads/main:expect-remote-hash origin <dst-hash>:refs/heads/main
# 同上,除非你的 main 不是分支
git push --force-with-lease=refs/heads/main:expect-remote-hash origin <dst-hash>:main
# 推送本地 main 分支所有提交
git push --force-with-lease=refs/heads/main:expect-remote-hash origin main
以第一個指令作為範例,因為他最完整,意思是設定需要被檢查的遠端是 refname
,並且預期他的 hash 應該是 expect-remote-hash
,推送到名為 origin 的遠端,只推送到 dst-hash
後續不推送。
他內部還有一些規則:
- 設定
refname
代表只保護指定的refname
歷史不被覆蓋,否則保護全部 refs - 如果連
expect-remote-hash
都一起設定,那麼要求refname
的 hash 和其完全相同
我們一般不會這麼仔細設定,而是直接使用
git push --force-with-lease origin main
介紹 --force-if-includes
由於 lease 只檢查遠端歷史是否出現在本地儲存庫中,所以在 lease push 前只要用了 fetch 就等於 force push,於是 --force-if-includes
擴展了 --force-with-lease
,額外檢查目前分支的 reflog 是否包含遠端的最新提交。
--[no-]force-if-includes Force an update only if the tip of the remote-tracking ref has been integrated locally.
如果 --force-with-lease[=<refname>[:<expect-remote-hash>]]
兩個參數都提供則 --force-if-includes
會變成 no-op。
這東西 2020 就有了結果到現在 2025 繁體中文資訊等於零。你別說 force-if-includes,連 refname
和 expect
都沒人講過他們的用途。
有趣小知識
不知道大家有沒有想過 lease 這個奇怪名字到底是在租什麼,命名由來是
- 想像使用 fetch 等同於獲得 ref 的租約
- 租了這個 refs 代表其屬於自己,可以隨意使用
- 別人 push 後有更新的提交,租賃失效,所以不能覆寫了
我只能說真會想,滿有創意的,來源在這裡。