跳到主要内容

强制推送

哪些场景需要使用强制提交?

  1. 修改最近一次 commit message:
git commit --amend

如果你刚刚推送了一个 commit,但随后发现有错别字或漏掉了某个文件,你可能会在本地执行: git commit --amend 这会产生一个全新的 Commit ID。此时,你的本地历史和远程历史已经分叉,普通的 push 会被拒绝,你必须强制推送来更新远程记录。

  1. 变基:
git rebase [分支]

为了保持提交历史的整洁(线性),经常在合入主分支前对自己的功能分支进行 rebase。

  1. 合并/清理提交历史:
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 --forcegit 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>