Git 安全强推:--force-with-lease
改写历史(rebase、squash、reset)之后必须强推,但 git push -f 是把双刃剑:它会把远程分支直接覆盖成你本地的样子,别人在这期间推上去的提交,在你不知情的情况下被抹掉。
--force-with-lease 只比 -f 多一个检查,代价极小,收益很大。
一、它检查什么
git push --force-with-lease origin feature-x
被比较的是你本地的 origin/feature-x 引用(远程跟踪分支)和远程当前实际值:
- 两者一致 → 说明没有人动过,允许覆盖
- 两者不一致 → 说明远程有新提交,拒绝推送
一句话:"我以为远程就停在我上次看到的位置,如果不对,别动"。
二、为什么不直接 -f
git fetch # 假设这时远端有人在推
git push -f origin feature-x # 直接覆盖,同事的提交没了
换成 --force-with-lease 就会报错:
! [rejected] feature-x -> feature-x (stale info)
这时先 git fetch 看一眼发生了什么,再决定要不要强制覆盖。
三、更严格的写法
# 显式指定期望的远程 SHA,最严格
git push --force-with-lease=feature-x:9c8d7e6 origin feature-x
适合脚本化场景:明确知道自己要覆盖到哪个版本。
四、常见坑
后台自动 fetch 会让 lease 失效:IDE 或某些工具会定时 git fetch,远程跟踪分支被刷新后,lease 检查就失去意义了(因为本地的"我以为"被更新了)。
结论:--force-with-lease 是防呆,不是安全保障。在共享分支上强推之前,永远先 git fetch 手动确认。
--force-with-lease 也有失败的可能:你会看到 stale info 或 rejected (non-fast-forward),别急着加 -f,先搞清楚远程多出来的提交是什么。
五、设成默认习惯
git config --global alias.pushf "push --force-with-lease"
或者设成新版本的默认行为:
git config --global push.useForceIfIncludes false # 保持默认即可
六、该不该强推的判断
| 分支 | 强推 |
|---|---|
| 自己的功能分支、PR 还没被 review | 可以 |
| PR 已经被 review,只是追加修改 | 可以,但要在 PR 里说明 |
| 多人共用的开发分支 | 不要 |
main / master / release/* | 绝对不要,走 PR |
记住一条:强推的前提是只有你在往这个分支上推。