Skip to content

07 · 远程同步

远程是别人能看见的那份历史。fetch 只更新你的「望远镜」,push 才改服务器。大多数「把远程搞坏了必须重拉项目」都发生在这一章。

git fetch

作用: 从远程把新提交、新分支、新标签下载下来,更新 origin/* 这些远程跟踪分支。

改动范围: 仅远程跟踪分支。不动当前工作区,不移动你的 main

典型场景: 先看看远程发生了什么,再决定 merge 还是 rebase;CI 合了 PR 之后更新本地望远镜;清理已经在远程删掉的分支缓存。

常用写法:

bash
git fetch origin
git fetch --all
git fetch --prune                # 远程已删的分支,本地 origin/xxx 也删掉
git fetch origin pull/123/head:pr-123   # 有的托管平台可这样取 PR

看远程比我多了什么:

bash
git log --oneline HEAD..origin/main
git diff HEAD...origin/main

不要这样用:fetchpull 当成一回事,以为 fetch 之后工作区代码会变。

做错了怎么办: fetch 几乎无害。prune 多删了跟踪分支,再 fetch 一次即可(远程还在的话)。

git pull

作用: fetch + 把远程跟踪分支合进当前分支。默认是 merge;若配置了 pull.rebase=true 则是 rebase。

改动范围: 远程跟踪分支 + 本地当前分支(可能产生合并提交或改写本地未推送提交)。

典型场景: 自己的功能分支或刚 switchmain,要把远程已有更新拿进来。

常用写法:

bash
git pull
git pull --ff-only               # 只能快进,否则失败(适合更新 main)
git pull --rebase
git pull origin main

更稳妥、可拆开决策的写法:

bash
git fetch origin
git log --oneline --graph HEAD...origin/main
git rebase origin/main           # 或 git merge origin/main

不要这样用:

  • 本地有未提交改动时盲目 pull(先 commit 或 stash)。
  • 在已经分叉的 mainpull 出一堆只有你自己懂的合并提交。用 --ff-only,失败再想为什么本地 main 会分叉。
  • pull --rebase 用在别人也在写的共享分支上,等于改写已推送历史。

做错了怎么办:

  • 合并冲突:git merge --abortgit rebase --abort(看 status 提示你在哪种状态)。
  • pull 产生了不想要的合并提交且未再 push:git reset --hard ORIG_HEAD

git push

作用: 把本地提交上传到远程,并更新远程分支指针。

改动范围: 远程。 本地不动。

典型场景: 功能分支第一次上台;日常同步;PR 更新。

常用写法:

bash
git push -u origin HEAD          # 推当前分支并建立跟踪
git push
git push origin main
git push origin --delete feature/login
git push origin v1.2.0           # 推一个标签,见第 10 章

rejected (non-fast-forward) 表示远程有你没有的提交。这是保护,不是故障:

bash
git fetch origin
git log --oneline --graph HEAD...origin/main
# 然后 merge 或 rebase,再普通 push

不要这样用: 看到 rejected 就 --force。那会丢掉远程上你还没拿回来的提交。

git push --force-with-lease(相对安全的强制推送)

作用: 仅当远程分支仍是你上次 fetch 见到的那个尖端时,才允许非快进更新。

改动范围: 远程历史改写。

典型场景: 你独享的 feature/login 上做了 rebase / amend,需要更新 PR。

bash
git fetch origin
git push --force-with-lease origin feature/login

git push --force · 危险

无条件把远程指针挪到你本地。远程上比你新的提交会变成无人引用(托管平台有时还能在网页上找到 SHA,Git 本身不保证)。

不要main / master / release/* 使用。很多平台会保护默认分支,直接拒绝。

做错了怎么办:急救手册 · force push 覆盖了别人的提交。预防手段就是永远用 --force-with-lease,并且先 fetch

跟踪关系断了

git statusno upstream

bash
git push -u origin HEAD
# 或本地已有远程分支:
git branch -u origin/feature/login

ahead 2, behind 3:两边都有独有提交,先 fetch,再 rebase 或 merge,再 push。不要 force。

推到了错误的远程或错误的分支

bash
git remote -v
git branch -vv
  • URL 错了:git remote set-url origin <正确地址>,再推一次。
  • 分支名错了:在正确分支上推,必要时 git push origin --delete 错的分支(确认没人在用)。
  • 提交不该公开:按泄露处理(密钥轮换),再考虑是否删除远程分支。历史一旦 push,假设已经被人拉走过。

和托管平台的关系

GitHub / GitLab / Gitee 的「保护分支」会禁止直接 push main,或禁止 force push。这是好事。日常应:

  1. 在功能分支上开发并 push。
  2. 开 Pull / Merge Request。
  3. 审查通过后由平台合并。
  4. 本地 switch main && git pull --ff-only

网页上点的 Rebase / Squash merge,会在远程产生新的 SHA。本地旧的功能分支不要再 merge 回 main,直接更新 main 后删本地分支。


上一章:06 · 改写历史 · 下一章:08 · 暂存工作区与多工作树