■ space · 散文道
git fetch upstream && git merge upstream/main && git push origin main — 这就是全部同步动作.
偏爱线性历史?把 merge 换成 git rebase upstream/main + push --force-with-lease.
fork“同步不了”?Missing upstream remote 缺失是头号原因——接一次线,见下文。
流水线
同步永远单向流动——upstream → 你的本地 main → origin。fork 里的东西不会流回 upstream;那是 pull request 的职责。
origin = 你的 fork · upstream = 你 fork 自的项目——常见错误是把两个 remote 都指向你的 fork;这样“同步”会悄无声息地什么也不做,因为你 fetch 的是一个和你一样陈旧的副本
第 1 步——接好 remote(每个克隆一次)
# 在你的 fork 本地克隆里
git remote add upstream https://github.com/ORIGINAL_OWNER/REPO.git
git remote -v # 确认:origin -> 你的 fork,upstream -> 原仓库
正确的 URL 在原仓库页面的绿色 Code 按钮下。接线是永久的——以后每次要敲的就是下面这些。
第 2 步——FETCH、MERGE、PUSH
git checkout main
git fetch upstream
git merge upstream/main
git push origin main
验证:git rev-list --count main..upstream/main → merge 前 12 → merge 后 0
快进是正常结局——你的 main 没有新东西,直接滑到 upstream/main,不产生 merge 提交。合并前想看落后多少:fetch 之后跑 git rev-list --count main..upstream/main。整套流程就这些;第 3 步是记得它存在:git fetch upstream && git merge upstream/main && git push origin main.
merge 还是 rebase?
两者把同样的代码落进你的 fork;区别在留下的历史。每个仓库选定一种策略并坚持:
| 方法 | 命令 | 历史结果 | 最适合 |
|---|---|---|---|
| merge | git merge upstream/main | 分叉分支上多一个 merge 提交 | 带开放 PR 的功能分支——绝不重写任何东西 |
| rebase | git rebase upstream/main | 你的提交重放在其上,历史线性 | 保持 fork 的 main 干净;想重置的分叉 fork |
| GitHub 界面 | Sync fork 按钮 / PR 合并 | Same as merge | 不开克隆的快速追赶 |
# rebase 变体,线性历史:
git fetch upstream
git rebase upstream/main
git push --force-with-lease origin main
必须强推,因为 rebase 重写了提交 ID——fork 的远端分支不再派生自本地分支。永远优先 --force-with-lease over --force:若期间有人(或你的另一台机器)推送过,它会拒绝覆盖远端,这让危险命令默认变安全。黄金法则:never rebase a branch that has an open pull request unless you know what you are doing — rebasing changes commit IDs, which can detach an open PR from its commits. Sync main 用 merge 同步,PR 分支别掺和。
“我的 fork 同步不了”——四个惯犯
- 没有
upstreamremote —git remote -vshows onlyorigin。症状:git fetch upstreamfails with'upstream' does not appear to be a git repository. Fix: step 1. - fetch 了却没 merge ——fetch 更新本地的
upstream/main,但不碰任何工作分支。症状:fetch 成功后git log看起来还是旧的。解法:git merge upstream/main. - 历史分叉 — you committed to your fork's
main,upstream 也动了。若想让 upstream 赢,解法:git reset --hard upstream/main——会丢弃你只在本地 main 上的提交,先用分支备份;如果错误的 reset 已经发生,git reflog还记得旧端点。 - rebase 之后推送被拒为非快进 — you rebased but pushed normally. Fix:
git push --force-with-lease origin main.
第五种罕见情况:upstream 仓库改名或删除,连第 1 步的 URL 都 404。GitHub 会重定向改名仓库,所以硬失败通常意味着被删或转私有——没有东西可同步了。
那个按钮
是的,网页能做:你的 fork 页面上,只要分支落后,分支下拉框旁就有 Sync fork button whenever your branch is behind; one click pulls upstream in. Below the fold, the same thing works as a pull request from 按钮,一点就把 upstream 拉进来。再往下,同样的事也可以通过从 into your fork's main。按钮的局限说明了何时退回 CLI:它只做快进或 merge——不会 rebase,分支分叉时干脆拒绝,让你丢弃提交或改用命令行。它也只同步默认分支。超出简单追赶的一切,还是那三条命令。
多久一次?
每次新工作之前——这是老实的答案:从新鲜的 main 开分支,你打开的任何 PR 都不会以“这基于三周前的版本”开场。对积极贡献的 fork,每天或每个会话同步一次 main 只要几秒。只读或只部署的 fork,等 upstream 出了你想要的东西再同步——订阅原仓库的 releases 动态,发版即同步。同步便宜恰恰因为它是例行公事;落后六个月的 fork 往往需要动手术而不是 merge——“帮我同步 fork”就是这样变成一个下午的。
08速查表
# 一次性配置
git remote add upstream https://github.com/ORIGINAL_OWNER/REPO.git
# 例行同步(merge 策略)
git fetch upstream && git merge upstream/main && git push origin main
# 例行同步(rebase 策略,线性历史)
git fetch upstream && git rebase upstream/main && git push --force-with-lease origin main
# 我落后多少?
git fetch upstream && git rev-list --count main..upstream/main
# 分叉到无法挽回——让 main 与 upstream 一致(破坏性)
git fetch upstream && git reset --hard upstream/main && git push --force-with-lease origin main
把那两行例行操作刻进肌肉记忆,分叉的 fork 就永远只是读物里的奇闻,而不是你要修的问题。
同步页到此为止 · 未取一分毫 · 一切皆本地