20 — 远程进阶:强制推送的保险绳——force-with-lease 写在前面这一章要解决什么你写完代码git push却被拒! [rejected] main - main (non-fast-forward)。本章帮你理解推送被拒的原因学会安全处理避免覆盖他人代码。学完后你应能解释「非快进」区分fetch和pull知道何时用git pull --rebase理解--force的危险以及--force-with-lease的安全理解远程跟踪分支配置多远程走通三角工作流读者设定大一同学已学过基本远程协作想搞懂推送被拒与强制推送。1. 定位为什么要单开一章讲远程进阶1.1 一句话先记住推送被拒 你想往信箱塞信但信箱里已经有别人的信了。你的提交的前驱是 A但远程 A 后面已有 BGit 拒绝覆盖。1.2 没有这些知识会怎样场景不知道怎么办知道以后push 被拒慌乱乱敲命令先拉再推想强推覆盖--force会丢失同事代码--force-with-lease安全兜底fetch 和 pull 分不清每次 pull 产生合并提交按需选择历史更干净多人改同一分支互相覆盖越推越乱理解远程跟踪分支有条有理1.3 生活里的类比你已经会的远程进阶对应往信箱塞信push信箱里已有别人的信塞不进去非快进先取出信箱里的信再塞自己的fetch merge / pull强行把别人的信换成自己的force push危险换之前先确认信箱里还是你上次看到的那几封force-with-lease1.4 本章内容目标目标你能做到理解非快进说清「远程有新提交你本地没有」区分命令fetch 只下载pull fetch 整合安全强推用--force-with-lease代替--force看懂跟踪分支解释origin/main是什么多远程协作配置 upstream走通三角工作流2. 本质推送被拒到底发生了什么2.1 一张图先建立感觉图本地与远程之间的同步——fetch 拉取push 推送。图分布式版本控制——每个人都有完整副本。2.2 用白话拆开远程仓库是共享的「中心信箱」。远程跟踪分支如origin/main是你本地对远程分支的缓存快照由fetch更新。推送是把本地提交上传到远程。非快进是远程有你不包含的新提交Git 拒绝推送。2.3 非快进到底是什么你上次看信箱信 Aorigin/main指向 A。现在信箱A → B别人推了 B。你要塞A → C你的本地提交 C 基于 A。C 未包含 B若硬塞B 丢失。Git 拒绝这就是非快进。2.4 fetch vs pull关键区别命令干什么动了哪些指针git fetch只下载远程提交和对象只更新远程跟踪分支git pullfetch 自动合并更新远程跟踪分支 合并到本地分支白话fetch是「先看看信箱里有啥新信」pull是「看完后自动归档到你的文件夹」。多数时候先fetch再决定如何整合。2.5 几条硬规矩规矩白话远程跟踪分支不可手动提交origin/main只能由 fetch 更新push 前先 fetch看看远程是否有新东西永远不要对共享分支用--force会覆盖别人的提交--force-with-lease只用于自己的功能分支保险绳不是通行证push 被拒是 Git 在保护你别慌先拉再推2.6 新手最常踩的坑坑表现办法push 被拒就--force别人提交丢失先fetch 整合实在要强推用--force-with-lease分不清 fetch 和 pull产生大量合并提交想看再用fetch想一步到位用pull建议配--rebase不知道origin/main是什么以为是远程分支实际是本地快照记住由 fetch 更新的记忆改了已推送的提交又推被拒历史变了变基后--force-with-lease多人推同一分支互相覆盖各开功能分支走 PR/MR3. 建议学习顺序理解非快进 → fetch 和 pull 的区别 → pull --rebase → 远程跟踪分支 → force-with-lease → 多远程 → 三角工作流4. 动手准备要求 Git 2.43.0使用本地模拟远程仓库无需联网。# 建假远程mkdir-p/tmp/fake-remote.gitcd/tmp/fake-remote.gitgitinit--bare-bmain# 建本地工作区mkdir-p/tmp/my-workcd/tmp/my-workgitinit-bmaingitconfig user.nameAda Examplegitconfig user.emailadaexample.comgitremoteaddorigin /tmp/fake-remote.git# 初始提交并推送printfv1\nfile.txtgitaddfile.txtgitcommit-minit: 第一个版本gitpush-uorigin main输出显示远程分支已建立本地main跟踪origin/main。5. 跟着做一次完整远程进阶实验5.1 制造非快进模拟别人先推了代码创建另一个工作区模拟 Bobmkdir-p/tmp/other-workcd/tmp/other-workgitclone /tmp/fake-remote.git.gitconfig user.nameBob Examplegitconfig user.emailbobexample.comprintfv2 from Bob\nfile.txtgitaddfile.txtgitcommit-mfeat: Bob 的修改gitpush回到你的工作区在不知情下提交并推送cd/tmp/my-workprintfv2 from Ada\nfile.txtgitaddfile.txtgitcommit-mfeat: Ada 的修改gitpush推送被拒提示non-fast-forward。这是非快进。5.2 先 fetch 看看远程有什么gitfetch输出显示origin/main更新到 Bob 的提交但本地main未变。git log --oneline --graph --all显示分叉远程origin/main指向 Bob本地main指向 Ada。5.3 用 pull 整合merge 方式gitpull产生合并提交。若遇到冲突解决后 push 成功。5.4 用pull --rebase代替 pullgitfetchgitpull--rebase把你的提交移到远程最新之后历史成一条直线。设置默认gitconfig pull.rebasetrue5.5 远程跟踪分支是什么git branch -r列出origin/maingit branch -vv显示本地分支与远程跟踪的关联。远程跟踪分支是只读的由fetch更新。5.6--force为什么危险变基后推送被拒git push --force会覆盖远程可能丢失他人提交。5.7--force-with-lease保险绳--force-with-lease要求远程与你本地origin/main一致否则拒绝。若别人在你 fetch 后推送了再--force-with-lease也会被拒保护了他人代码。5.8 远程管理查看、添加、删除git remote -v查看远程git remote rename重命名git remote remove删除git remote add添加5.9 多远程与三角工作流你 fork 了老师的仓库为origin老师的仓库为upstream。从upstream拉取往origin推送通过 PR 贡献。gitremoteaddorigin https://github.com/你的用户名/课件作业.gitgitremoteaddupstream https://github.com/老师用户名/课件作业.gitgitfetch upstreamgitmerge upstream/maingitpush origin main设置本地main跟踪origin/main。6. 命令按「用途」分组6.1 拉取与整合命令干什么git fetch下载远程更新不动本地分支git fetch --all下载所有远程的更新git pullfetch mergegit pull --rebasefetch rebase推荐git config pull.rebase true让 pull 默认用 rebase6.2 推送命令干什么git push正常推送git push -u origin main推送并设置上游跟踪git push --force强制推送危险git push --force-with-lease安全强推远程变了就拒绝6.3 远程管理命令干什么git remote -v列出所有远程git remote add 名字 地址添加远程git remote remove 名字删除远程git remote rename 旧名 新名重命名远程6.4 查看跟踪关系命令干什么git branch -r列出远程跟踪分支git branch -vv列出本地分支及其跟踪关系git branch --set-upstream-toorigin/main main设置上游跟踪7. 对照表降低记忆负担7.1 push 被拒时怎么办情况做法命令远程有新提交我落后了先整合再推git pull --rebase→git push我改写了历史自己的分支安全强推git push --force-with-lease改写了历史共享分支别推用revert或沟通不关心历史不推荐git push --force极度危险7.2 fetch vs pull vs pull --rebase命令下载了代码合并了历史形状git fetch是否不变git pull是是merge多一个合并提交git pull --rebase是是rebase一条直线7.3 --force vs --force-with-lease命令会覆盖别人的提交吗安全吗git push --force会不检查危险git push --force-with-lease远程有新提交就拒绝有保险绳7.4 远程跟踪分支 vs 本地分支概念例子谁更新它你能直接提交到上面吗远程跟踪分支origin/maingit fetch不能本地分支main你自己能8. 安全习惯8.1 建议这样做习惯原因push 前先git fetch查看远程是否有新东西设置pull.rebase true避免无意义合并提交用--force-with-lease代替--force多一层保护--force-with-lease只用于自己的功能分支共享分支不要强推强推前先fetch确保origin/main最新保险绳才有效push 后用git log确认确保推了你想推的8.2 绝对不要做的事操作后果对共享分支--force别人提交丢失不fetch就--force-with-lease保险绳可能失效推送包含密钥的提交密钥泄露历史难清8.3 推荐工作流团队协作1. 开始干活前git fetch 2. 整合远程git pull --rebase 3. 开分支改代码、提交 4. 推之前再 fetch 一次 5. 正常推送git push 6. 如果改写了历史自己的功能分支git push --force-with-lease9. 真实场景9.1 课程作业和同学共用一个仓库各自开功能分支不直接推main提 PR 让助教或组长审若 push 被拒git pull --rebase再推9.2 开源贡献三角工作流fork 项目到自己的账号git remote add upstream 原仓库地址git fetch upstreamgit checkout -b fix/xx upstream/main改好提交git push -u origin fix/xx在 GitHub 上从你的 fork 发 PR 到 upstream9.3 推送被拒但急着想推gitfetchgitlog--onelineorigin/main..HEAD# 查看本地独有提交gitpull--rebasegitpush9.4 变基后要强推在自己的功能分支上 rebase 后推送被拒使用git fetch git push --force-with-lease origin feature/xxx。9.5 误推了敏感信息删除文件并提交但历史仍存在紧急情况需重写历史见后续章节立即通知团队不要拉取。10. 稍微多懂一点点可选远程跟踪分支保存在.git/refs/remotes/origin/main由 fetch 更新。--force-with-lease比较本地origin/main与远程最新哈希不一致则拒绝故需先 fetch。可通过git remote set-url --push origin 禁止推送的地址防止误推。git push --force-if-includesGit 2.30更严格要求本地已整合远程提交。即使被强推覆盖远程 reflog 可能找回提交。11. 小实验请一定动手实验甲制造并解决非快进用假远程和两工作区A 推B 推被拒Bfetch后pull --rebase再推成功。实验乙force-with-lease 拦截B 不 fetch 直接--force-with-lease被拒fetch 后如有 A 新提交仍会被拒需整合再推。实验丙三角工作流一个仓库当 upstream一个当 fork从 upstream 拉推到 origin模拟 PR。通过标准能独立处理推送被拒知道何时用--force-with-lease能说出它比--force安全在哪里。12. 常见问题问 1推送被拒是不是我做错了不是远程有你不包含的新提交先 fetch 再整合。问 2fetch 和 pull 到底用哪个想看再决定用fetch想一步到位用pull --rebase。建议配置pull.rebase true。问 3--force-with-lease一定能保护我吗在 fetch 后、别人又推了这种时间差内可能拦不住但比--force安全得多。问 4能不能对main用--force-with-lease技术上能但共享分支不应强推用revert更安全。问 5pull 出了合并提交怎么办设置pull.rebase true以后不会出现。问 6origin/main和main有什么区别origin/main是本地对远程 main 的快照main是你本地分支。问 7删了 remote 会删代码吗只删关联关系不删本地代码和远程仓库。问 8三角工作流和普通推送有什么区别普通推送直推主仓库三角工作流推自己的 fork再通过 PR 合并。13. 总结、学习路线与思维升华13.1 这一章请记住的点记住什么非快进远程有你不包含的提交推送被拒是 Git 在保护你fetch vs pullfetch 只下载pull fetch 整合pull --rebase避免合并提交保持历史一条线–force危险直接覆盖别人的提交–force-with-lease安全强推远程变了就拒绝远程跟踪分支origin/main是本地快照由 fetch 更新多远程origin upstream三角工作流下一章额外命令速查21 章13.2 在整个系列中的位置01 认识 Git 02 三区模型 03 基本工作流 04 安全地撤销 05 分支与合并 06 远程协作 …… 19 暂存与清理 20 远程进阶 ← 当前 21 额外命令速查 22 冲突策略 23 安全实践 ……13.3 思维升华保险绳不是鼓励你冒险而是当你必须强推时多一道确认。推送被拒是 Git 在说「远程有新东西你还没看」不是在跟你作对。先 fetch 看清楚再决定怎么整合。能不强推就别强推必须强推就用--force-with-lease。信箱类比塞信前先看看信箱里还是不是你记得的样子——这就是 force-with-lease 的全部意义。13.4 参考资料Pro Git 中文版 — 远程分支Pro Git 中文版 — 与远程协作官方文档git push / fetch / pull / remote命令输出样例验证环境Git 2.43.0演示作者信息Ada Example adaexample.com13.5 本章检查清单能说清「非快进」是什么为什么推送会被拒能区分fetch和pull会用git pull --rebase保持历史干净知道--force-with-lease比--force安全在哪里能解释origin/main是什么会添加和删除远程仓库能说出三角工作流的三个步骤推送被拒时知道该怎么处理按回车前先预判状态——这一章的保险绳是你对远程仓库的清醒认识。