强制推送
哪些场景需要使用强制提交?
- 修改最近一次 commit message:
git commit --amend
如果你刚刚推送了一个 commit,但随后发现有错别字或漏掉了某个文件,你可能会在本地执行: git commit --amend 这会产生一个全新的 Commit ID。此时,你的本地历史和远程历史已经分叉,普通的 push 会被拒绝,你必须强制推送来更新远程记录。
- 变基:
git rebase [分支]
为了保持提交历史的整洁(线性),经常在合入主分支前对自己的功能分支进行 rebase。
- 合并/清理提交历史:
git rebase -i [基准分支]
gt rebase -i HEAD~N
在将代码合并到主分支之前,为了让历史更好看,你可能会把 10 个琐碎的提交“压缩”成 1 个提交。这种操作本质上也是改写历史,需要强制推送。
主要有两种强制推送命令:
git push origin branch_name --force-with-lease
git push origin branch_name --force
但是这两种推送方式原理大不相同:
| 命令 | git push --force | git push --force-with-lease |
|---|---|---|
| 执行逻辑 | 直接覆盖。 不管远程分支是否有他人提交,强制用本地历史替换远程历史。 | 检查后再覆盖。 仅当远程分支自你上次 fetch 以来没有新提交时,才允许覆盖。 |
| 安全性 | 低。 极易意外抹掉同事刚刚推送的代码。 | 高。 如果有你不知道的新提交,Git 会拒绝推送并提醒你。 |
git push --force 是最原始的强力手段,它告诉远程仓库:“别废话,把我现在的版本设为最终(最新)版本。”
比如你在 feature 分支上做了一些修改,你的同事小王也在这个分支上修了一个紧急 Bug 并推送到远程了。此时你执行 --force 强制推送,小王的修复会被你本地最新的提交覆盖彻底消失...
git push --force-with-lease 是官方推荐的更安全的做法。
当你执行此命令时,Git 会检查你本地记录的“远程分支位置”(即 origin/feature)是否与服务器上真正的远程分支位置一致。
如果一致 说明自你上次更新以来,没有人在远程推送过新代码。这时候强制推送是安全的。
如果不一致 说明有人在你之后推送了代码。Git 会绝推送并报错:stale info(信息已过期)。你需要先 fetch 最新的更改才能推送。
一个致命的“坑”:自动 Fetch
虽然 --force-with-lease 很安全,但有一个常见情况会使它失效:
如果你使用的 IDE(如 VS Code 或 JetBrains IDE)配置了“后台自动 Fetch”,或者你手动运行了 git fetch 但没有去看更新了什么,那么 --force-with-lease 会认为你“已经知晓了最新的更改”,从而允许覆盖。
更极致的安全做法:如果你想绝对确保不覆盖某次特定的提交,可以指定 commit ID 推送:
git push --force-with-lease=branch_name:<commit_id>