Skip to content

远程仓库与协作

本章要点

  • 远程仓库的本质:一份用于同步的"公共副本"
  • 远程操作全家桶:clone / remote / fetch / pull / push
  • fetch vs pull 的区别(重要)
  • 上游分支(upstream)与跟踪关系
  • 推送被拒绝(non-fast-forward)的处理
  • 标签(tag)与发布

远程仓库是什么

text
          ┌─────────────────────────┐
          │  远程仓库(origin)       │
          │  GitHub / GitLab / Gitee │
          └──────────┬──────────────┘
                     │ push / pull / fetch
        ┌────────────┴────────────┐
        ▼                         ▼
   你的本地仓库                 同事的本地仓库
  • 远程仓库 = 一份用于大家同步的公共副本,不是"权威",只是"约定俗成的中心"。
  • clone 时 Git 自动把远程仓库命名为 origin

管理远程仓库

bash
$ git remote -v                          # 查看远程仓库地址
# origin  https://github.com/user/repo.git (fetch)
# origin  https://github.com/user/repo.git (push)

$ git remote add origin <url>            # 添加远程仓库
$ git remote set-url origin <新url>      # 修改远程地址
$ git remote remove origin               # 移除远程仓库
$ git remote rename origin upstream      # 重命名

fetch:只下载,不合并

bash
$ git fetch origin            # 把远程最新提交下载到本地,但不动工作区
  • fetch 后远程的新提交出现在 origin/main 之类的远程跟踪分支上。
  • 你的工作区、本地分支完全不受影响
  • 想对比/合并远程内容时再手动操作:
bash
$ git log origin/main..HEAD           # 本地领先远程的提交
$ git merge origin/main               # 把远程内容合并进当前分支

💡 fetch 是"只读"操作,任何时候都安全,可以随便执行。

pull:fetch + merge

bash
$ git pull                    # 等价于 git fetch + git merge
$ git pull origin main        # 明确指定拉取来源分支
  • 一步完成"下载远程更新 + 合并进当前分支"。
  • 可能触发合并冲突(解决方式同第 4 章)。
bash
$ git pull --rebase           # 用 rebase 方式整合(见第 7 章,团队常配置为默认)

⚠️ 理解 fetch 与 pull 的区别是面试高频题: fetch 只下载不改动你的工作;pull 会直接把远程内容合并进当前分支(可能冲突)。

push:把本地提交推上去

bash
$ git push                    # 推送到当前分支的"上游分支"
$ git push origin main        # 明确推送到 origin 的 main
$ git push -u origin main     # -u 设置上游分支(第一次推送时用)

上游分支(upstream)概念

  • 本地分支可以"跟踪"一个远程分支,即建立 upstream 关系
  • 建立后,直接 git push / git pull 无需再写远程名和分支名。
bash
$ git push -u origin feature/login   # 第一次推送并建立跟踪
$ git branch -vv                     # 查看跟踪关系
# * feature/login 8f4a2c9 [origin/feature/login] feat(login): ...

推送被拒绝:non-fast-forward

如果远程已经有了你本地没有的提交(同事先推送了),push 会被拒绝:

text
$ git push
! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to ...
hint: Updates were rejected because the remote contains work that you do
hint: have locally. This is usually caused by another repository pushing
hint: to the same ref.

正确处理流程(先拉后推):

bash
$ git pull --rebase origin main    # ① 拉取远程更新,把自己的提交垫到最上面
# (如有冲突则解决,然后 git rebase --continue)
$ git push                         # ② 再推送

⚠️ 不要用 git push --force 强行覆盖远程(除非你清楚自己在做什么,见 5.7 与第 7 章)。

协作中的常见场景

场景一:同事推送了新代码,我怎么拿到

bash
$ git pull            # 一步到位
# 或分步(更可控)
$ git fetch
$ git log --oneline origin/main..HEAD   # 先看自己领先多少
$ git merge origin/main

场景二:我推不上去(别人先推了)

bash
$ git pull --rebase   # 拉取 + 把我的提交重放到远程最新之上
$ git push            # 重新推送

场景三:远程删除了一个分支,本地还有残留

bash
$ git fetch --prune origin   # 清理已不存在的远程分支引用
$ git branch -r              # 查看远程分支

场景四:Fork 协作(开源项目常见)

text
别人的仓库(upstream)──fork──► 你的远程仓库(origin)
                                   │ clone

                              你的本地仓库
bash
$ git remote add upstream <原仓库url>        # 把原仓库加为 upstream
$ git fetch upstream
$ git checkout -b fix-xxx upstream/main      # 基于 upstream 最新代码开分支
# ...开发提交...
$ git push origin fix-xxx                    # 推到自己的远程
# 然后去平台发起 Pull Request(见第 9 章)

标签(Tag):给提交打"里程碑"

标签用于标记发布版本(v1.0.0 等),分为:

  • 轻量标签:只是一个指针。
  • 附注标签:包含打标签者、时间、说明,可签名(推荐用于发布)。
bash
$ git tag                          # 列出所有标签
$ git tag v1.0.0                   # 创建轻量标签
$ git tag -a v1.0.0 -m "第一个正式版本"   # 创建附注标签
$ git tag -a v1.0.0 <提交哈>     # 给历史提交打标签
$ git push origin v1.0.0           # 推送单个标签
$ git push origin --tags           # 推送所有标签
$ git tag -d v1.0.0                # 删除本地标签
$ git push origin :refs/tags/v1.0.0   # 删除远程标签
$ git checkout v1.0.0              # 检出标签(处于 detached HEAD,详见 6.4)

💡 发布流程惯例:代码合并到 main → 打 v版本号 标签 → push 标签 → 触发 CI/CD 构建发布。

force push 与它的风险

git push --force-f)用本地提交覆盖远程历史:

bash
$ git push --force origin feature/login
  • ⚠️ 会重写远程历史,如果别人基于旧历史开发,会造成混乱。
  • ⚠️ 永远不要对共享分支(main/master/develop)force push。
  • 只对自己的特性分支使用,且提前与团队沟通。
  • 更安全的替代:git push --force-with-lease——只在远程没有被别人动过时才覆盖,否则拒绝(强烈推荐用它替代 --force)。

常见远程错误与解决

错误提示原因解决
non-fast-forward远程有本地没有的提交git pull --rebasegit push
Permission denied (publickey)SSH 密钥未配置生成并配置 SSH key(见下)
Could not resolve host网络/DNS 问题检查网络与代理
fatal: remote origin already exists重复添加 origingit remote set-url origin <url>
Updates were rejected because the tip of your current branch is behind同上 non-fast-forward先 pull 再 push

SSH 密钥配置(免密推送)

bash
$ ssh-keygen -t ed25519 -C "you@example.com"   # 生成密钥(一路回车即可)
$ cat ~/.ssh/id_ed25519.pub                     # 复制公钥内容

到 GitHub → Settings → SSH and GPG keys → New SSH key 粘贴保存。