GitHub Pull Request:设计与使用
Pull Request(PR)是 GitHub 的核心协作机制——一个"请你 pull 我的代码"的请求。 这一页讲清:① 什么是 PR(提议合并 + 触发 review)② PR 的 7 个组件(title / description / commits / diff / review / checks / conversation)③ PR 完整生命周期(draft → review → approval → merge)④ 最佳实践(小而专注、关联 issue、draft for WIP)⑤ PR 为什么是 SWE-bench 题目源。读完你就能"看 PR / 提 PR / review PR"——这 3 个动作是开源协作的最小单元。
概览
| 项目 | 说明 |
|---|---|
| 本卡定位 | 协作基础 · 1.0 章节 第 2 张 |
| 前置 | github-vs-git.html(Git vs GitHub 区别) |
| 读完会什么 | 能读懂 / 提一个 PR;能用 PR 流程做代码 review;理解为什么 SWE-bench 题目都长成"issue + PR"形态 |
| 姊妹讲义 | build-problem-from-pr.html(从 PR 构造题目) |
前置认知:PR 是给谁用的?
在正式学"PR 是什么"之前,先破除一个常见误解——很多人以为 PR 是"留给团队以外的人"用的(比如开源项目里路人给项目贡献代码)。这是只看到了一半。PR 的真实定位是:所有"代码合入前需要审查"的场景的通用机制——团队内外都在用,而且团队内部用得更频繁。
PR 的核心作用一句话:在代码正式合入主分支之前,先让别人看一眼、讨论一下、确认没问题再合。至于"别人"是谁——团队内外都可能是。它要回答的问题不是"谁有资格提代码",而是"这行代码凭什么能进 main"。
| 团队内部 PR(更常见) | 团队外部 PR(开源协作) | |
|---|---|---|
| 谁发 | 有写权限的同事 | 无写权限的外部人(需先 fork) |
| 目的 | Code Review + CI + 保护主分支 | 让维护者审查外部贡献 |
| 要不要 fork | 不用(同仓开个分支即可) | 必须 fork(复制一份到自己账号下改) |
| 频率 | 日常开发的主流方式 | 开源项目的贡献入口 |
- 代码审查(Code Review):同事帮你检查 bug、风格、设计问题——多一双眼睛,少一堆线上事故
- 留下讨论记录:为什么这么改、权衡了什么,都留在 PR 评论里,半年后还能查
- 触发 CI 自动测试:PR 一开,自动跑测试,绿了才合——把"能跑"从口头承诺变成机器把关
- 保护主分支:谁也不能直接往 main 上推,必须经过 PR 这道门
- 外部人没有仓库写权限,不能直接推代码
- 路径:fork(复制仓库到自己账号)→ 在自己仓库改 → 向原仓库发 PR → 维护者审查后决定 合并 / 要求修改 / 拒绝
- 这就是你向 SWE-bench 这类上游项目贡献代码时走的路
(PR 是"将代码改动合并进项目"的提议。PR 是 GitHub 的核心协作特性,让你在合并前讨论和审查改动。)——注意官方用的是"collaboration feature(协作特性)",并没有限定"只给外部人用"。
(如果你想为 PR 新建分支、但没有该仓库的写权限,可以先 fork 这个仓库。)——这句话反过来看就是:有写权限的人不需要 fork,直接在仓库里开分支发 PR(即团队内部场景);没写权限的人才走 fork(即团队外部场景)。
- Fork and pull model(fork-拉取模型):"anyone can fork an existing ('upstream') repository if they have read access"(任何有读取权限的人都可以 fork 上游仓库)——开源项目常用,对应"场景 2 · 团队外部"
- Shared repository model(共享仓库模型):"collaborators have push access to a single shared repository and create topic branches... Pull requests are useful in this model because they start code review"(协作者对同一个共享仓库有推送权限,改动时开 topic 分支;PR 在这个模型里用于发起 code review)——小团队 / 私有项目常用,对应"场景 1 · 团队内部"
出处:GitHub Docs · About pull requests 与 Creating a pull request。
§ 1 什么是 PR:一个"请你 pull 我的代码"的请求
Pull Request(PR) 的字面意思就是"拉取请求"——你(贡献者)告诉项目维护者:"请把我在 X 分支上的改动拉(pull)到主分支"。
- 源分支(base / head):你想把哪个分支的改动合到哪个分支
- 一组 commits:你做的代码改动(按时间顺序排列)
- 讨论 + 审批:维护者可以在你的改动上逐行评论,最终决定 merge / close
# PR 在 Git 层面是什么? # 假设你想把 feature-branch 合并到 main: # 1. 你推 feature-branch 到 remote git push origin feature-branch # 2. PR 触发后,GitHub 帮维护者做的事: # · 计算 feature-branch 和 main 的差异(git diff main..feature-branch) # · 展示 commits 列表(git log main..feature-branch --oneline) # · 维护者点击"Merge pull request" → 等价于: git checkout main git merge --no-ff feature-branch # --no-ff 保留 PR 合并的拓扑 # 3. PR 的"价值"不在 git 命令(merge 谁都会), # 而在 PR 流程(review / checks / conversation)——这些是 GitHub 加的协作层
§ 2 PR 的 7 个组件
一个完整的 PR 包含 7 个组件——每个都有明确作用。
| 组件 | 是什么 | 作用 |
|---|---|---|
| 1. Title(标题) | 一句话描述改动 | 让 reviewer 一眼看出"这 PR 在做什么" |
| 2. Description(描述) | 详细说明——含 "What / Why / How" + 关联 issue | 让 reviewer 不看代码也能理解意图 |
| 3. Commits(提交列表) | 该分支相对 base 的所有 commits(按时间) | 展示"代码是一步步怎么改过来的" |
| 4. Diff(差异) | 每个文件的改动行——增 / 删 / 修改 | reviewer 实际审的内容 |
| 5. Review(评审) | reviewer 的评论 + approval / requested changes | 核心质量保证——多人审一遍 |
| 6. Checks(CI(Continuous Integration,持续集成)检查) | GitHub Actions 跑测试 / lint / build | 自动化质量门禁——不通过不能 merge |
| 7. Conversation(讨论) | PR 内的所有评论和回复(不限于代码行) | 整个 PR 讨论历史的"审计日志" |
| Tab | 对应组件 | 用途 |
|---|---|---|
| Conversation | Title / Description / Conversation / Review 汇总 | 看 PR 整体讨论 + 决策 |
| Commits | 所有 commits 列表 | 看历史改动的时间线 |
| Files changed | Diff + 行内评论 | 实际 review 代码 |
| Checks | CI 跑的结果 | 看自动化测试通过没 |
好的 PR:1 个 feature + 3-5 个 commit + 清晰的 description
坏的 PR:1 个 commit 改 50 个文件 + "fix stuff" 作为 title
§ 3 PR 完整生命周期
PR 从创建到关闭,经历 5 个状态——可以无限循环在第 3-4 步(review 反复打回)。
┌──────────────────────────────────────────────────────────────────┐
│ 1. Draft(草稿) │
│ → 开发者创建 PR,但标记为 Draft("还在做,别 review") │
│ → 通常用于 WIP(work in progress)——CI 可以跑但不被 merge │
│ → 想跟别人提前讨论,但不是"请 review" │
└──────────────────────────────────────────────────────────────────┘
↓ (标为 "Ready for review")
┌──────────────────────────────────────────────────────────────────┐
│ 2. Open(开放) │
│ → PR 正式进入 review 阶段 │
│ → 通知 reviewer(@mention / 自动指派) │
│ → CI 自动触发(GitHub Actions 跑测试) │
└──────────────────────────────────────────────────────────────────┘
↓ (reviewer 提评论 / 打回)
┌──────────────────────────────────────────────────────────────────┐
│ 3. Review(评审)—— 可能多轮 │
│ → Reviewer 提 comments(提问 / 建议 / 必改) │
│ → 开发者 push 新 commit 响应评论 │
│ → Reviewer 改评论状态:approve / request changes / comment │
│ → CI 重新跑(验证新 commit 没破) │
│ → ↻ 循环直到所有 reviewer approve(或 maintainer 强 merge) │
└──────────────────────────────────────────────────────────────────┘
↓ (所有阻塞条件解除)
┌──────────────────────────────────────────────────────────────────┐
│ 4. Approved(已批准) │
│ → 达到合并条件:required reviews 数通过 + CI 全部绿 │
│ → Maintainer 点击 "Merge pull request" │
└──────────────────────────────────────────────────────────────────┘
↓
┌──────────────────────────────────────────────────────────────────┐
│ 5. Merged(已合并)/ Closed(关闭) │
│ → Merged:PR 合到目标分支——完成 │
│ → Closed:PR 没合(被拒绝 / 弃用 / 改用其他方式)——关闭 │
└──────────────────────────────────────────────────────────────────┘
| 方式 | 效果 | 适用 |
|---|---|---|
| Merge commit(默认) | 保留所有原始 commits + 1 个 merge commit | 开源项目 / 多人协作 / 想保留完整历史 |
| Squash and merge | 把 PR 内所有 commits 压成 1 个 | 主分支想保持"每个 commit = 一个独立改动"——干净 |
| Rebase and merge | 把 PR 内 commits 一个个 rebase 到 base(无 merge commit) | 想保持线性历史(A → B → C 一条线) |
§ 4 PR 的 4 个关键概念(必须分清)
| 概念 | GitHub 名 | 等价概念(其他平台) | 区别 |
|---|---|---|---|
| Pull Request | GitHub / Bitbucket | GitLab: Merge Request(MR) | 名字不同,本质完全一样——都是"提议合并" |
| Fork | ✓ | GitLab: ✓(同名) | 把别人仓库复制到你的账号下——提 PR 的前提 |
| Issue | ✓ | GitLab: ✓(同名) | 任务追踪——可独立存在,也可关联到 PR("fixes #123") |
| Draft PR | ✓ | GitLab: "WIP" prefix 传统 | 明确标记"还在做"的 PR |
| 关键字 | 效果 | 例子 |
|---|---|---|
| fixes #123 | PR merge 后自动关闭 #123 issue | 修 bug 用 |
| closes #123 | 同 fixes——也关闭 | 同 fixes |
| resolves #123 | 同 fixes——也关闭 | 同 fixes |
| refs #123 | 只关联,不自动关闭 | 部分相关 / 跟进 issue |
| see #123 | 同 refs | 同 refs |
§ 5 PR 最佳实践(写 PR 的 6 条铁律)
提一个"让人愿意 review"的 PR,是工程基本功。下面 6 条铁律,新手老手都适用。
- 小而专注:一个 PR 只做一件事——修一个 bug / 加一个 feature / 重构一个模块。PR 改的代码行数 < 400 行,超过 800 行的 PR 几乎一定会被 reject("拆成几个 PR")
- Title 写清"做了什么":动词 + 范围 + 简短说明
· 好:"Fix: handle None gracefully in user lookup"
· 差:"fix stuff" / "update code" / "WIP" - Description 写清"为什么 + 怎么做的":4 段式
· What:改了什么
· Why:为什么改(issue 链接)
· How:怎么改的(思路 / 关键决策)
· Test:怎么测试的 - 关联 issue:用 fixes #123 关键字——PR merge 后自动关 issue
- Draft for WIP:还在做时用 Draft——不要让 reviewer 看半成品
- 响应评论要快:reviewer 等你回 comment 的时间越长,PR 越容易"stale"被关
## What Fix the bug where `get_user()` raises AttributeError when user.email is None. ## Why Issue #1234 reported that some users (registered via social login) have no email set, causing the user dashboard to crash. ## How - Add `is not None` check before accessing `.email` - Add unit test for the None case - Update docstring to document the new behavior ## Test - All existing tests pass - New test `test_get_user_with_no_email` passes - Manual test on staging: dashboard no longer crashes Closes #1234
§ 6 PR vs Merge Request(GitLab / Gitea 术语)
不同平台叫法不同,本质完全一样。
| 平台 | 术语 | 其他叫法 | 备注 |
|---|---|---|---|
| GitHub | Pull Request(PR) | — | 最广泛使用 |
| GitLab | Merge Request(MR) | 也支持 PR 别名 | 强调"合到主分支" |
| Bitbucket / Gitea / Gitee | Pull Request | — | 同 GitHub |
| 名字 | 支持者论点 | 反对者论点 |
|---|---|---|
| Pull Request(GitHub) | 字面意思"我请求你 pull"——强调主动请求 | "pull" 命令只是 fetch + merge,含义不够准 |
| Merge Request(GitLab) | 字面意思"我请求你 merge"——强调合并动作 | "merge" 也不够准(实际是 review 决策) |
§ 7 PR 为什么是 SWE-bench 题目源
这一节是核心——把"PR 流程"和"SWE-bench 题目构造"打通。先对齐名词:SWE-bench(SWE = Software Engineering,软件工程;bench = benchmark,基准测试)是"用真实 GitHub issue 评测代码 agent"的软件工程基准,本教学平台的评测主线(详见 SWE-bench 入门)。
| PR 元素 | SWE-bench 字段 | 作用 |
|---|---|---|
| Issue body(PR 通过 fixes #123 关联的) | problem_statement | 题面(agent 看到的问题描述) |
| PR 描述 + commits | patch(即 git diff base_commit..head) | 答案(ground truth) |
| PR 引入的 test 文件改动 | test_patch | 评测的测试(用于定义 F2P) |
| PR 合并时的 base branch commit | base_commit | 评测起点(agent 从这个状态开始改) |
GitHub Issue + PR
┌──────────────────────────────────────┐
│ Issue: 标题 + body(问题描述) │
│ PR: title + description + commits │
│ + diff + review + checks │
│ + conversation │
└──────────────────────────────────────┘
↓ 提取
┌──────────────────────────────────────┐
│ SWE-bench Instance(一条) │
│ instance_id: "repo__issue-1234" │
│ repo: "django/django" │
│ base_commit: PR merge base SHA │
│ problem_statement: issue body │
│ patch: PR diff │
│ test_patch: PR 引入的测试改动 │
│ fail_to_pass: 新引入的失败测试 │
│ pass_to_pass: 回归测试集 │
└──────────────────────────────────────┘
标题 + body(问题描述)"] PR["PR
title + description + commits
diff + review + checks"] end EXT["提取
(构造脚本)"] subgraph SWE["SWE-bench Instance 字段"] PS["problem_statement
= issue body"] BC["base_commit
= PR merge base SHA"] PA["patch
= PR diff(源码改动)"] TP["test_patch
= PR 引入的测试改动"] F2P["FAIL_TO_PASS
= 新引入的失败测试"] P2P["PASS_TO_PASS
= 回归测试集"] end ISS --> EXT PR --> EXT EXT --> PS EXT --> BC EXT --> PA EXT --> TP TP --> F2P TP --> P2P
引用与配套资料
| 来源 | 链接 / 路径 | 说明 |
|---|---|---|
| GitHub 官方 PR 文档 | docs.github.com/en/pull-requests | PR 完整功能文档 |
| GitHub PR 教程 | github.com/features/code-review | 官方 PR 功能介绍 |
| GitHub Flow | docs.github.com/en/get-started/quickstart/github-flow | PR-based 工作流 |
| Conventional Commits | conventionalcommits.org | PR 命名规范(feat / fix / chore 等前缀) |
| 本仓姊妹讲义 | github-vs-git.html | Git vs GitHub 区别 |
| 本仓姊妹讲义 | build-problem-from-pr.html | 从 PR 构造题目 |
| 本仓姊妹讲义 | swe-bench-intro.html | SWE-bench 是什么 |
| 本仓姊妹讲义 | swe-bench-data-schema.html | SWE-bench 数据 schema |