GitHub 开源贡献 fork 与 PR 完整指南
总览
向开源项目贡献的标准路径是 fork 加 PR:fork 上游仓库到自己的账号,克隆到本地修改,推送到 fork,再向原仓库发起 pull request。整套流程围绕三个仓库角色展开。
一 fork 上游仓库
fork 是 GitHub 对任意公开仓库开放的操作,不需要特殊权限,任何登录用户都能执行。fork 之后你拥有一个完全属于自己账号的副本,对自己的 fork 有完整读写权限。
gh repo fork huggingface/tau --clone=false也可以在 GitHub 网页打开仓库页面点击 Fork 按钮。注意 fork 只是在创建那一刻复制上游快照,之后上游的更新不会自动进入你的 fork。
二 克隆与远程配置
克隆自己的 fork 到本地,然后添加两个远程仓库引用:origin 指向你的 fork,upstream 指向上游原仓库。
git clone https://github.com/Jack-monster/tau.git
cd tau
git remote add upstream https://github.com/huggingface/tau.git
git remote -v输出应同时包含 origin 和 upstream 两个远程。
三 同步上游代码
fork 不会自动跟踪上游,需要手动同步。同步的完整链路分三步,fetch 和 merge 只发生在本地,push 才是把结果送到远程 fork 的动作:
git fetch upstream
git merge upstream/main
git push origin mainfetch 把上游最新提交下载到本地,不改动任何分支;merge 把上游 main 合并进本地 main,只影响本地;push 把合并结果推送到你的 fork。三条命令合起来等价于网页端的 Sync fork 按钮,或一条 gh 命令:
gh repo sync Jack-monster/tau四 创建分支与修改
同步完成后,从最新的本地 main 切出功能分支,再做修改。分支必须基于合并后的最新代码,这样后续 PR 的差异最小、冲突最少。
git checkout -b fix/sidebar-title-theme-colors五 提交与推送
修改完成后提交到本地,再推送到 fork 的对应分支:
git add src/tau\_coding/tui/widgets.py src/tau\_coding/tui/app.py tests/test\_tui\_components.py
git commit -m "fix(tui): re-theme sidebar collapsible titles on theme switch (#693)"
git push origin fix/sidebar-title-theme-colors提交信息使用项目约定的前缀,例如 fix (tui): 或 feat:。
六 创建 PR
PR 的对比基准是上游 main 和你 fork 上的功能分支,GitHub 根据 fork 的 parent 关系识别两个仓库同源,base 指向原仓库 main,head 指向你的 fork 分支。创建后原仓库的 Pull requests 页面立即出现这条 PR,维护者收到通知。
gh pr create --base main --head Jack-monster:fix/sidebar-title-theme-colors \\
  \--title "fix(tui): re-theme sidebar skills/prompts titles on theme switch" \\
  \--body-file /tmp/pr\_body.md多行 Markdown 正文用临时文件以 --body-file 传递,避免转义问题。PR 正文写 Closes #693 可以关联并自动关闭对应 issue。创建后验证渲染结果:
gh pr view 694 --json body七 PR 处理与合并
创建 PR 后维护者收到通知
仓库 CI 对 PR 分支自动运行检查,结果展示在 PR 页面
维护者阅读 diff 提出评审意见,可能需要你修改
按意见修改后推送到同一个 fork 分支,PR 自动更新,不需要重建
维护者确认后执行合并,常见方式是 squash merge,把分支所有提交压缩成一个提交合入 main
合并后 PR 自动关闭,关联的 issue 也会自动关闭
你全程只控制自己的 fork,对原仓库唯一的动作是创建 PR 和回复评审,合并权限始终在维护者手里。
八 PR 未合并时上游更新了怎么办
PR 还没被合并,但 upstream/main 已经往前走了,此时需要让你的功能分支重新基于最新上游代码,避免 PR 出现冲突或 diff 过大。标准做法是 rebase,而不是 merge,这样 PR 历史保持线性,只包含你自己的提交。
第一步,拉取上游最新提交:
git fetch upstream第二步,更新本地 main。如果工作区有 uv.lock 这类镜像源自动改写的文件挡路,先用 git checkout -- uv.lock 还原,再 merge:
git checkout main
git merge upstream/main第三步,切回功能分支并 rebase:
git checkout fix/sidebar-title-theme-colors
git rebase mainrebase 会先把你的提交临时摘下来,把分支移动到最新 main,再把你的提交逐个重新应用。如果出现冲突,手动编辑解决后继续:
git add 冲突文件
git rebase --continue想放弃这次 rebase 回到原状态,执行 git rebase --abort。
第四步,rebase 改写了提交历史,本地提交 hash 全部变化,需要强制推送到 fork,PR 自动更新:
git push --force-with-lease origin fix/sidebar-title-theme-colors用 --force-with-lease 而不是 --force,它会检查远程分支是否被别人更新过,防止覆盖他人工作。
rebase 后建议重新跑一遍项目检查,确认和上游新代码没有隐性冲突:
uv run pytest -q
uv run ruff check .
uv run mypyfork 的 main 不需要单独更新,PR 比较的是 upstream/main 和功能分支,fork 的 main 是否陈旧不参与比较。只有希望 fork 页面与上游一致,或需要从 fork 远程 main 拉取时,才额外执行 git push origin main。
常见问题
fork 会自动跟踪上游吗
不会。fork 是创建那一刻的快照,同步必须手动执行。
本地合并后 fork 会自动合并吗
不会。git fetch upstream 和 git merge upstream/main 只更新本地仓库,fork 远程需要再执行 git push origin main 才会更新。
可以跳过 push origin main 直接做修改吗
可以。PR 比较的是上游 main 与 fork 功能分支的差异,fork 的 main 是否陈旧不参与比较。只要功能分支基于本地合并后的最新 main 切出,直接推功能分支创建 PR 即可。fork 的 main 可以一直不更新,不推也不影响 PR。
什么时候才需要同步 fork 的 main
希望 fork 页面与上游一致,或连续贡献多个 PR 需要从 fork 远程拉取时。仅为当前 PR 则非必须。
最短完整流程
gh repo fork huggingface/tau --clone=false
git clone https://github.com/Jack-monster/tau.git
cd tau
git remote add upstream https://github.com/huggingface/tau.git
git fetch upstream
git merge upstream/main
git checkout -b fix/your-branch-name
\# 修改代码后:
git add .
git commit -m "fix: description"
git push origin fix/your-branch-name
gh pr create --base main --head Jack-monster:fix/your-branch-name \\
  \--title "title" --body-file /tmp/pr\_body.md