本节摘要:网络命令只做两件事——传输对象、更新引用。remote 是配置里的昵称地址簿,fetch 下行同步并更新远程跟踪引用,pull 是 fetch 加合并,push 上行对象并请求挪动远端分支。理解"请求"二字,推送被拒就不再令人困惑。
先交代一个容易忽略的事实:Git 本身只是一个本地工具,网络能力是它后来长出的部分,而且长得极其克制——四条网络命令共享同一套"传对象、挪引用"的极简内核,没有任何一条命令会替你做合并决策。正因如此,学远程命令的正确姿势不是背参数,而是先想清楚"哪边是本地、哪边是远端、对象往哪个方向流、引用由谁更新"这四个问题。本节结束时你应当能够:
克隆或添加远端后,全部配置都摊在配置文件里,可以直接取证:
git remote -v # origin https://example.com/team/project.git (fetch) # origin https://example.com/team/project.git (push)
origin 不是关键字,只是默认昵称。fetch 与 push 地址分开配置是有意设计——镜像工作流里可以"从上游拉、往自己维护的仓库推";同一仓库也能登记多个远端(git remote add upstream ...),Fork 工作流(第 5 章)正是靠双远端运转。昵称之后,所有远程引用统一挂在 refs/remotes/<昵称>/ 命名空间下——这就是 origin/main 这种写法的全名。
fetch 干两件事:把远端有而本地没有的对象下载进对象库;把本地的 refs/remotes/origin/* 引用更新为远端各分支当前指向。它不碰你的工作区、不碰本地分支、不碰 index——是最安全的网络操作,随时可执行。
git fetch origin # From https://example.com/team/project.git # a1b2c3d..e5f6a7b main -> origin/main git status -sb # ## main...origin/main [behind 2]
behind 2 表示远端跟踪引用比你本地 main 多两个提交——注意此刻对象已经在你库里了,只是还没合入。想看新到了什么:
git log main..origin/main --oneline # 远端新提交清单 git diff main origin/main # 快照差异
这个"先 fetch 再观察后合并"的节奏,是团队协作的黄金动作。它把"接收别人的变更"从一次黑盒跳变,分解成了可审查的两步。
git pull 等于 fetch 之后自动对配对的本地分支执行 merge(或 rebase,取决于配置)。省事的代价是跳过了上面那个审查窗口——尤其多人同库高频推送的团队,盲目 pull 会把半懂状态的历史直接搅进本地。两个建议:其一,团队统一配置 git config pull.rebase true,pull 变成 fetch 加 rebase,历史更线性,也避免了大量无意义的 merge commit;其二,冲突多发的仓库干脆禁用 pull,改为显式的 fetch 加 merge/rebase 两步。
pull 的分发风险还有一个隐蔽形态:fast-forward 式 pull 在你本地"恰好"也有新提交时会静默升级成三方合并并可能弹冲突,惊慌中的随手解决容易埋错。回到第 3 章的原则——冲突解决前先 git log --merge -p 看清双方各自改了什么。
push 把本地对象上传,然后请求远端把它某分支的引用挪到你的提交。远端有权拒绝,最常见两类拒绝:
非快进拒绝:远端分支有你没有的提交(同事先推了)。这是引用模型的必然——远端把 main 从 X 挪到你的 Y,若 X 不是 Y 的祖先,除非强推否则是危险操作,服务端默认拒绝。规范处理:
git fetch origin git rebase origin/main # 把你的提交复印到同事的新顶点之后 git push origin main # 再推 现在是快进
权限拒绝:受保护分支(通常 main/release)不允许直接推。这是平台层的引用保护,对应第 5 章的功能分支工作流——推到自己的分支,走合并请求进门。
强推的安全阀再强调一次:git push --force-with-lease origin dev,仅当远端 dev 仍是你记忆中的位置才覆盖,防止踩掉同事刚推的工作。裸的 --force 只该出现在确定个人独占的分支上。
⚠️ 常见坑:pull 下来的合并冲突解决到一半取消(Ctrl-C 或关窗口),index 停在三舞台状态,之后的 status 全是 U 状态码。复原办法:
git merge --abort。养成"合并/变基中途不看懂 status 不动手"的习惯。
💡 关键直觉:把远端想成"同事的硬盘镜像"。fetch 是抄录他最新的书签位置(顺便把缺页复印过来),push 是请求他更新书签。你的本地 main 与他的 main 之间唯一的正式通道,就是你自己执行的 merge 或 rebase——Git 从不自动替你合并任何东西。
下一节把 origin/main 这个概念拆到底——三层分支命名与跟踪关系。