一次可靠的贡献,不是“把代码推上去”就结束,而是让问题、改动、审查、CI 结果和合并决策都能被别人复现和理解。
本文只讲 GitHub 上的仓库贡献路径:Issue、topic branch、Fork、Pull Request(PR)、Review、Required Checks 和合并后的清理。Git 的对象模型、分支基础和远程同步细节复用 Git 分支管理 与 Git 远程协同,不在此重复 Git internals。
1. 先选贡献模型:shared repository 还是 fork-and-pull#
GitHub 的 Pull Request 并不限定一种组织方式。常见模型有两种:
| 模型 | 本地与远程关系 | 谁创建 topic branch | 适合场景 |
|---|---|---|---|
| Shared repository | origin 指向共同仓库,贡献者拥有推送权限 |
贡献者在共同仓库创建并推送分支 | 小团队、组织内私有项目 |
| Fork-and-pull | upstream 指向原仓库,origin 指向自己的 fork |
贡献者在自己的 fork 创建并推送分支 | 开源项目、没有原仓库写权限的外部贡献者 |
两种模型都应当遵守同一条边界:不要直接在默认分支上开发。topic branch 将改动隔离出来,使 CI 和审查可以在合并前运行。
1.1 base、head 与 PR 的方向
PR 不是“把两个仓库合在一起”,而是请求把 head 分支的提交合入 base 分支:
base:目标仓库和目标分支,通常是维护者仓库的main。head:包含待审查改动的分支;可以是同仓库分支,也可以是 fork 中的分支。- PR 页面显示的
base <- head方向必须先确认,再创建。
例如,外部贡献者向教学示例项目提交修复时,方向通常是:
base: OWNER/example-theme-project:main
head: YOUR_GITHUB_LOGIN/example-theme-project:fix/123-compositor-note把方向选反,可能会把维护者仓库的 main 当成待合入内容,或把 PR 发到错误的 fork。提交前先在网页比较页面和 gh pr create 参数中各确认一次。
1.2 贡献前先读仓库的真实规则
贡献入口按以下顺序检查:
- README:项目目标、支持范围、运行方法、已知限制和当前请求。
CONTRIBUTING.md或文档中的开发章节:命名、测试、提交和审查要求。.github/ISSUE_TEMPLATE/与.github/PULL_REQUEST_TEMPLATE*:表单或 PR 描述要求。CODEOWNERS:哪些文件会自动请求谁审查。- Workflow 与分支规则:Required Checks、权限、是否必须保持分支最新。
仓库没有这些文件时,不要猜测隐藏流程;在 Issue 或 PR 描述中写清楚自己的假设、验证环境和未验证边界。
1.3 示例:贡献桌面兼容性说明
假设一个主题项目欢迎贡献者补充尚未实测的 compositor 兼容性,README 要求报告 Fcitx5、compositor 版本与复现现象。这是教学情境,没有对应的个人仓库、版本快照或测试结论。
如果项目没有 CONTRIBUTING、Issue/PR 模板或 CODEOWNERS,贡献者应以 README 和 PR 中可复核的事实为准,不猜测隐藏流程。某一个 compositor 已实测,也不能推断其他桌面已实测。对这类兼容性贡献,先在 Issue 中记录环境、现象和经脱敏的截图/日志,再决定是否提交代码,通常比直接开大 PR 更容易审查。
2. 从 Issue 到 topic branch#
2.1 先把问题变成可审查的任务
Issue 或贡献计划至少说明:现状与复现、期望行为、改动边界、相关 Issue,以及命令/版本/平台/截图/日志等验证方式。文档、主题和 CI 改动同样要写可重复的证明;“看起来更好”不能代替检查。
2.2 Shared repository:在共同仓库开分支
下面假设共同仓库远程名为 origin,默认分支为 main:
git switch main
git fetch origin
git pull --ff-only origin main
git switch -c fix/123-compositor-note
# 编辑文件、运行项目规定的本地检查
git status --short
git diff --check
git add path/to/changed-file
git commit -m "docs: clarify compositor contribution status"
git push -u origin HEADgit switch -c 会从当前提交创建并切换到 topic branch;因此,执行它前必须确认 main 已经同步到计划使用的 base。git push -u origin HEAD 将当前分支推送到 origin,并建立同名追踪关系,不需要手写一遍分支名。
2.3 Fork-and-pull:区分 origin 与 upstream
外部贡献者先 fork 原仓库,再克隆自己的 fork。建议从一开始就把两个远程命名清楚:
git clone https://github.com/YOUR_GITHUB_LOGIN/REPOSITORY.git
cd REPOSITORY
git remote -v
git remote add upstream https://github.com/OWNER/REPOSITORY.git
git fetch upstream
git switch main
git merge --ff-only upstream/main
git push origin main
git switch -c fix/123-compositor-note此处:
origin是自己的 fork,topic branch 应推到这里;upstream是原仓库,用来获取维护者的最新 base;upstream/main是本地记录的原仓库main,不是一个凭据或特殊服务器。
若自己的 fork 的 main 没有独有提交,git merge --ff-only upstream/main 会快进式对齐;存在分叉时它会拒绝整合;若 fork 的 main 有本地提交,先检查这些提交是否应保留,再决定 rebase、merge 或重新创建 fork。同步 fork 也可以使用网页上的 Sync fork 或:
gh repo sync YOUR_GITHUB_LOGIN/REPOSITORY -b main命令同步 fork 中指定的 main 分支;topic branch 仍需单独更新。
2.4 什么时候不要 rebase
git rebase upstream/main 适合个人、尚未被别人依赖的 topic branch,或尚未发布的本地分支。它会重写被重放提交的哈希,因此下列情况不要直接做:
- 不要 rebase 公共
main、受保护分支或别人正在使用的分支。 - 同一个 PR 的 head branch 已允许维护者或其他协作者提交时,先沟通再 rebase。
- 项目明确要求保留分支合流历史时,不要为了“线性”擅自改用 rebase。
- 只为绕过 CI 或审查而反复 rebase,会让评论对应的提交变得难以追踪。
如果你确认 topic branch 只有自己使用,rebase 后通常需要安全地更新远程:
git fetch upstream
git switch fix/123-compositor-note
git rebase upstream/main
# 解决冲突后:git add <file> && git rebase --continue
git push --force-with-lease -u origin HEAD--force-with-lease 会检查远程分支是否在上次获取后被别人更新;不要把 git push --force 当作普通同步命令,也不要对 main 使用强制推送。
2.5 提交前的最小检查
在开 PR 前,至少完成以下检查:
git status --short
git diff --stat upstream/main...HEAD
git diff --check upstream/main...HEAD
git log --oneline --decorate upstream/main..HEADShared repository 可以把上述 upstream/main 替换为 origin/main;关键是比较当前 head 与 PR 的实际 base,而不是与一个过时的本地分支比较。再运行 README 或仓库 CI 中明确要求的测试、构建和格式检查。
3. 创建 Pull Request#
3.1 Web 界面创建
网页创建适合第一次贡献、需要上传截图或仔细检查 base/head:先选 base 和 head 仓库/分支,再决定是否 Draft;标题写结果,正文写问题、验证、风险和未完成事项,最后检查 Files changed、Checks 与 Reviewers。
PR 正文至少写 Summary、Validation、Risk/limitations 和相关 Issue;可用 Fixes #123 建立合并后的自动关闭关系。若项目没有 PR 模板,贡献者应主动提供 compositor 兼容性、Fcitx5 版本、截图和“尚未实测”的平台。
3.2 使用 gh pr create
已在仓库目录、当前分支已推送时,可用 gh 创建 PR。shared repository 的 head 只写分支名;fork-and-pull 则要指出 fork 所有者:
gh pr create \
--repo OWNER/REPOSITORY \
--base main \
--head YOUR_GITHUB_LOGIN:fix/123-compositor-note \
--title "docs: clarify compositor contribution status" \
--body $'## Summary\n- Clarify tested and untested compositor paths.\n\n## Validation\n- Reviewed README and local documentation.\n\nFixes #123'创建后用以下命令检查 PR 和 CI:
gh pr checks 123 --watch3.3 PR 的可审查边界
一个 PR 最好只解决一个可说明的逻辑问题:
- 不把格式化、重命名、依赖升级和功能修复混在一起。
- 不把与 Issue 无关的本地配置、凭据、构建产物或临时调试文件带入 diff。
- 改 workflow、权限、Action 版本或构建发布路径时,必须在正文中单独标明。
- 文档贡献也要给出链接、命令和核验日期;不要把未经验证的桌面支持写成事实。
4. Review、Request changes 与更新 PR#
4.1 Review 的三种结果
审查者可以对整体 PR 或具体行发表评论,并提交三种状态:
| 状态 | 含义 | 作者下一步 |
|---|---|---|
| Comment | 提供意见,不表达合并批准或阻止 | 讨论、记录决定,必要时修改 |
| Approve | 认可当前改动可以合并 | 等待其他要求满足 |
| Request changes | 指出合并前必须处理的事项 | 修复、解释或与审查者达成明确结论 |
Request changes 是否真正阻止合并,取决于仓库的 branch protection 或 ruleset;不要只看一个 review 状态就断言“平台一定禁止合并”。Required reviews、Required Checks 和分支最新状态要一起看。
4.2 用 gh pr review 提交审查
审查者可以在终端提交整体意见:
# 只评论,不批准也不阻止
gh pr review 123 --comment --body "请补充失败路径的验证结果。"
# 修改已完成且满足审查范围
gh pr review 123 --approve --body "已检查 diff 与本地验证。"
# 合并前必须处理的问题
gh pr review 123 --request-changes --body "请先修复 workflow 权限,再重新请求 review。"具体行评论通常更适合网页的 Files changed 页面;终端命令适合整体结论或自动化辅助。审查应针对可验证的行为、回归风险和维护成本,不要以个人编码风格替代项目规则。
4.3 作者如何回应反馈
把每个评论分类:需要修改、需要解释、超出当前范围或应另开 Issue。修改后推送到同一 head branch,PR 会自动更新并重新运行相关 checks:
git switch fix/123-compositor-note
# 编辑并运行本地检查
git diff --check
git add path/to/changed-file
git commit -m "docs: address review feedback"
git push
gh pr view 123 --comments完成一轮较大修改后,可以在网页中 Re-request review。对话已解决不等于代码已经验证;在回复中写出“改了什么、用什么命令验证、哪个检查仍失败”。
如果需要把 topic branch 重新对齐 base:
git fetch upstream
git rebase upstream/main
# 解决冲突、运行检查后
git push --force-with-lease -u origin HEAD只有在 head branch 的使用者知情且远程确实属于自己时才这样做。若维护者已经在分支上提交,应优先使用合并或请维护者决定同步方式。
5. Required Checks、CI 与合并#
5.1 把 CI 当作合并条件,而不是装饰
PR 的 Checks 页面显示自动化测试、构建、格式检查和安全扫描。Required Checks 是 base 分支规则要求必须通过的状态;本地检查通过不代表 GitHub 上的检查已经通过,也不代表权限、矩阵任务和合并队列条件已满足。
在合并前确认:PR 非 Draft,所需 review 已满足,Required Checks 成功,base/head 无冲突且满足“必须最新”规则;同时确认 fork workflow 没有因权限、审批或取消而未完成,并重新检查 PR diff 是否仍对应 Issue。
命令行检查示例:
gh pr checks 123 --required
gh pr checks 123 --watch
gh pr view 123 --json state,isDraft,mergeable,reviewDecision,statusCheckRollup,baseRefName,headRefName如果 CI 失败,先区分三类原因:代码或测试失败、workflow 配置失败、Runner/依赖的暂时性故障。不要用重跑掩盖确定性失败,也不要为“变绿”删除检查或放宽权限。
5.2 选择合并方式
最终合并方式由仓库规则和维护者决定:
| 方式 | 结果 | 常见适用情形 |
|---|---|---|
| Merge commit | 保留 PR 内各提交,并增加合并点 | 需要保留完整分支历史 |
| Squash and merge | 将 PR 压成 base 上一个提交 | 一个 PR 对应一个逻辑变更 |
| Rebase and merge | 将各提交线性放到 base 上 | 提交已经清晰且项目要求线性历史 |
不要因为个人偏好绕过仓库设置。拥有合并权限且确认要求全部满足时,gh pr merge 可以明确选择方式:
# 由维护者确认后执行;会压缩并删除 head branch
gh pr merge 123 --squash --delete-branch
# 满足规则后自动合并(仓库允许且项目方明确使用时)
gh pr merge 123 --auto --squash --delete-branch若只是贡献者而没有写权限,提交 PR 后等待维护者合并;不要尝试 --admin 绕过规则。合并后的 PR、评论和提交历史仍然保留,即使 head branch 被删除。
6. 合并后同步与清理#
确认 PR 已合并后,再清理本地和远程 topic branch。shared repository 使用 origin/main 更新;fork-and-pull 通常这样处理:
git switch main
git fetch upstream
git merge --ff-only upstream/main
git push origin main
# 先确认 PR 已合并且分支提交不再需要;squash/rebase 合并后 -d 可能拒绝。
git branch -d fix/123-compositor-note
git push origin --delete fix/123-compositor-note有些仓库会在合并后自动删除远程 head branch,此时最后一个删除命令可能提示分支不存在;先用 git branch -r 或 GitHub 页面确认,不要为了消除提示而强制操作。若 PR 被关闭而没有合并,先保留分支和提交,确认不再需要后再删除。
7. Fork PR 与 Actions 的安全边界#
Fork PR 的代码来自当前仓库控制范围之外,维护者必须把它当作不可信输入。对公开仓库触发 pull_request 的 fork workflow,通常没有普通仓库 Secrets,GITHUB_TOKEN 权限也应按只读理解;具体权限仍取决于仓库和事件配置。首次贡献者的 workflow 还可能需要维护者批准后才运行。
贡献者和维护者都应遵守以下边界:
- 不在 PR、Issue、命令参数或日志中提交 PAT、SSH 私钥、云密钥或其他凭据。
- 不为了让 fork PR 发布制品而把写权限 token 或生产 Secrets 注入普通测试 job。
- 审查
.github/workflows/的改动,特别是run:、第三方 Action、权限声明和 artifact 处理。 - 谨慎使用
pull_request_target:它在 base 仓库上下文运行,可能拥有 Secrets 或更高权限;不要在这种特权 workflow 中 checkout 并执行不可信的 fork 代码。 - 需要发布、部署或写入仓库的流程应与不可信 PR 的测试流程分开,并在受控事件或环境中运行。
安全性不应被简化成“fork 没有 Secrets,所以可以随便跑”。工作流本身、依赖安装、构建脚本和上传的 artifact 都可能执行贡献者提供的内容;权限最小化和事件选择要一起审查。
8. 常见误区速查#
| 误区 | 后果 | 修正 |
|---|---|---|
直接修改 main |
无法隔离审查,可能触发错误发布 | 从最新 base 创建 topic branch |
| 忘记确认 base/head | PR 发到错误仓库或错误分支 | 创建前检查仓库、分支和 diff |
Fork 只配置 origin |
不知道如何获取原仓库更新 | origin 指 fork,upstream 指原仓库 |
用 git pull 随意合入 |
产生意外 merge commit 或覆盖未提交工作 | 先 fetch,按项目策略使用 --ff-only、merge 或 rebase |
对公共分支 push --force |
覆盖他人提交 | 公共分支禁止;个人 PR 分支也只用 --force-with-lease |
| PR 混入无关格式化 | 审查噪音大,难以定位回归 | 拆成独立 PR 或独立提交 |
| 只写“已测试” | 别人无法复现验证 | 写命令、版本、平台、结果和限制 |
| CI 失败就删除检查 | 质量门失效 | 先定位代码、workflow 或基础设施原因 |
| 把 Request changes 当成绝对锁 | 误解平台规则 | 同时检查 branch protection/ruleset 和 review 权限 |
把 pull_request_target 当作普通 PR 事件 |
不可信代码可能接触特权上下文 | 不在特权 workflow 中执行 fork 代码 |
官方资料#
- GitHub Flow
- Pull requests
- About pull request reviews
- Pull request merges
- Merging a pull request
- Syncing a fork
- Secure use reference for GitHub Actions
- Using secrets in GitHub Actions
gh pr creategh pr reviewgh pr mergegh pr checks
参考与延伸
- GitHub Flow ↗
- Pull requests ↗
- About pull request reviews ↗
- Pull request merges ↗
- Merging a pull request ↗
- Syncing a fork ↗
- Secure use reference for GitHub Actions ↗
- Using secrets in GitHub Actions ↗
- gh pr create ↗
- gh pr review ↗
- gh pr merge ↗
- gh pr checks ↗
根据技术笔记重新整理,示例使用通用名称。