Chapter 04

远程仓库

连接世界——理解 fetch、pull、push 的本质差异与团队协作工作流

远程仓库是什么

远程仓库(Remote Repository)是托管在网络上的 Git 仓库,可以是 GitHub、GitLab、Gitee,也可以是你自己搭建的服务器,甚至只是你本地文件系统上的另一个路径。

关键认知:远程仓库本质上与本地仓库没有区别,它们都是完整的 Git 仓库。"远程"只是一个地址的别名,代表另一个 Git 仓库的位置。

一个本地仓库可以关联多个远程仓库——例如,你 fork 了一个开源项目后,可以同时关联自己的 fork(origin)和原始上游仓库(upstream)。

管理远程仓库(git remote)

# 查看当前所有远程仓库
git remote -v

# 添加远程仓库(别名 + URL)
git remote add origin https://github.com/user/repo.git

# 添加上游仓库(fork 工作流)
git remote add upstream https://github.com/original/repo.git

# 修改远程 URL(例如从 HTTPS 切换到 SSH)
git remote set-url origin git@github.com:user/repo.git

# 重命名远程
git remote rename origin github

# 删除远程
git remote remove upstream

# 查看远程仓库详情
git remote show origin

origin 的约定origin 只是一个约定俗成的默认名称,代表你最主要的远程仓库(通常是你克隆的那个仓库)。git clone 会自动将克隆源命名为 origin。你完全可以给它取任何名字,只是全球开发者都默认使用 origin

三个容易混淆的命令

这是初学者最常混淆的地方,务必彻底理解它们的区别。

git fetch — 只下载,不修改工作区

git fetch 从远程下载最新的提交历史,但不会修改你的工作区或本地分支。它只更新远程追踪分支(如 origin/main)。

# 拉取 origin 的所有分支更新
git fetch origin

# 拉取所有远程的更新
git fetch --all

# 拉取后查看远程有哪些新内容
git log main..origin/main --oneline
fetch 前:
origin/main ──▶ A ── B
main(本地) ──▶ A ── B

远程有了新提交 C、D,fetch 后:
origin/main ──▶ A ── B ── C ── D (已更新)
main(本地) ──▶ A ── B (未变化,你可以检查后再决定如何合并)

适用场景:想先看看远程有什么变化,再决定是否要合并。

git pull — fetch + merge(或 rebase)

git pull = git fetch + git merge(默认),它下载远程更新并立即合并到当前分支。

# 默认:fetch + merge
git pull origin main

# 更推荐:fetch + rebase(保持线性历史)
git pull --rebase origin main

# 配置 pull 默认使用 rebase
git config --global pull.rebase true

git push — 推送本地提交到远程

git push 将本地分支上的新 commit 推送到远程仓库。

# 推送到 origin 的 main 分支
git push origin main

# 首次推送,同时建立追踪关系(-u / --set-upstream)
git push -u origin main

# 之后有了 -u 追踪关系,可以简写为:
git push

远程追踪分支

远程追踪分支(如 origin/main)是本地仓库对远程分支状态的"快照",只在 fetch/pull/push 时更新。

# 查看所有分支的追踪关系
git branch -vv

# 输出示例:
#   main       a3f2c1d [origin/main] feat: add profile page
#   feature    c7d1e3f [origin/feature: ahead 2, behind 1] work in progress
#   local-only 8e4b9a2 local experiment

推送的安全性

普通推送与强制推送

# 推送功能分支
git push origin feature-login

# 强制推送(危险!会覆盖远程历史,可能导致他人丢失工作)
git push --force origin feature-login

# 安全的强制推送(推荐替代 --force)
# --force-with-lease 在远程有新提交时会拒绝,防止覆盖他人工作
git push --force-with-lease origin feature-login
!

永远不要对 main/master 分支执行 --force push!这会改写所有团队成员依赖的共享历史,导致无法挽回的混乱。强制推送只应在你独自维护的个人功能分支上使用,且首选 --force-with-lease

Pull Request / Merge Request 工作流

PR(GitHub 术语)或 MR(GitLab 术语)是团队协作的核心机制,它不是 Git 的功能,而是代码托管平台提供的工作流工具。

功能分支工作流(团队常用)

# 1. 从最新的 main 创建功能分支
git switch main && git pull
git switch -c feature/user-profile

# 2. 开发功能,提交
git add . && git commit -m "feat: add user profile page"

# 3. 推送功能分支到远程
git push -u origin feature/user-profile

# 4. 在 GitHub/GitLab 上创建 Pull Request
#    base: main  ←  compare: feature/user-profile

# 5. 等待 Code Review,根据反馈修改并继续 push
git commit -m "fix: address review comments"
git push

# 6. PR 被批准后,在平台上点击 Merge
# 7. 删除功能分支
git branch -d feature/user-profile

Fork 工作流(开源贡献)

原始仓库(upstream)
↓ Fork(在 GitHub 上点击)
你的 Fork(origin)
↓ git clone
本地仓库
# 克隆你的 fork
git clone git@github.com:your-username/repo.git

# 添加原始仓库为 upstream
git remote add upstream https://github.com/original/repo.git

# 定期同步原始仓库的更新
git fetch upstream
git switch main
git merge upstream/main

# 创建功能分支,开发,推送到自己的 fork
git switch -c feature/my-contribution
git push origin feature/my-contribution

# 然后在 GitHub 上从 your-fork 向 original 发起 PR

解决远程冲突

最常见的场景:你推送时,远程已有他人提交(push 被拒绝)。

# 推送被拒绝,提示 "remote contains work that you do not have locally"
git push origin main
# ! [rejected] main -> main (fetch first)

# 解决方案 1:先 pull 再 push(使用 rebase 保持线性历史)
git pull --rebase origin main
# 如果有冲突,解决冲突后:
git add .
git rebase --continue
git push origin main

# 解决方案 2:普通 pull(会产生 merge commit)
git pull origin main
git push origin main