第 03 章

Pull Request 全流程

从开分支到合并进主干——GitHub 协作的核心载体

PR 不是技术,是一种工作礼仪

直接推主干?

你改完代码想提交,但团队不允许直接动 main 分支

一人项目随便推

你自己写作业,commit 直接 push 到 main,没问题。但只要超过一个人,这么干迟早撞车。

多人直接推的灾难

A 和 B 同时改 main,互相覆盖;有人推了没测过的代码,全组项目跑不起来;出了问题不知道是谁干的。没有审查,就没有质量。

想知道 PR 是啥

你听人说「提个 PR」,但不知道那具体是什么。它和 branch 什么关系?和 commit 什么关系?

本章把 PR 讲透:从「为什么要开分支」到「合并策略怎么选」,再到「冲突怎么解」。
PR 是 GitHub 上一切团队协作的基础,也是后续 04-06 章的前置知识。

开始之前

适合谁

  • 会 push/pull,但没开过 PR
  • 想参与需要 PR 的开源或团队项目
  • 被「分支、合并、冲突」吓到过的人

你将学会

  • feature branch 工作法的完整节奏
  • 在 GitHub 上开 PR、写描述、做 review
  • 选择 Merge / Squash / Rebase 三种合并策略
  • 处理合并冲突(网页端 + 命令行)

本章暂不覆盖

  • 分支保护规则(第 05 章)
  • fork 模型的 PR(第 06 章)
  • CI 自动检查(第 07 章)

PR 是什么

Pull Request = 「请把我的分支拉进去合并」的申请

图:PR 的本质
大白话:你在主干旁边开一条「小路」(分支),在小路上随便改,改完申请「请把我这条小路接回大路」。这个申请就是 PR。别人可以先看看小路修得好不好,再决定接不接。

分支工作法

feature branch——每做一个改动就开一条专属分支

# 1. 确保在 main 上且是最新
$ git checkout main
$ git pull

# 2. 开一条新分支,名字说清要干嘛
$ git checkout -b feature/add-login

# 3. 在新分支上正常改代码、commit
$ git add .
$ git commit -m "添加登录页面"

# 4. 推送新分支到远程
$ git push -u origin feature/add-login
分支命名约定:用 功能/简述 格式,如 feature/login、fix/typo、docs/readme。别人一看就知道这条分支是干嘛的。

在网页开 PR

推送分支后,GitHub 会提示你创建 Pull Request

入口

  • 推送分支后,终端会显示一个 Create a pull request 链接
  • 或仓库页面 → Pull requests 标签 → New pull request

关键设置

  • base:合并到哪(通常 main)
  • compare:你的功能分支
  • 标题:一句话说清做了什么
  • 描述:改了什么、为什么改、怎么测
好描述让 review 更快。别只写「改了点东西」。写清楚「解决了什么问题、做了哪些改动、如何验证」三段,reviewer 会感谢你。

PR 描述模板

一个清晰 PR 描述的骨架,照着填就行

## 改动说明
添加了用户登录页面,支持邮箱+密码登录。

## 改动类型
- [x] 新功能
- [ ] bug 修复
- [ ] 重构

## 关联 issue
Closes #12

## 如何测试
1. 启动项目 `npm start`
2. 访问 /login
3. 用 test@example.com / 123456 登录

## 截图
(如有 UI 改动,贴截图)
Closes #12 是关键字联动:合并这个 PR 时,第 12 号 issue 会自动关闭。第 04 章 Issues 会详细讲这个机制。

PR 界面导览

一个 PR 页面有四个关键标签

标签看什么用途
Conversation 讨论、状态、合并按钮 整体协调与最终合并
Commits 这个 PR 含哪些提交 看工作过程
Files changed 逐行 diff 对比 code review 主战场
Checks CI 自动测试结果 第 07 章 Actions 产出

90% 的 review 工作发生在 Files changed 标签里。

做 code review

在 Files changed 标签里逐行审查代码

三种评论方式

  • 鼠标悬停某行 → 点蓝色 + 图标加单行评论
  • 拖选多行 → 对一段代码整体评论
  • 评论框右下角选类型:Comment(普通)、Suggest change(建议改法)

Suggestion 的妙用

用 ```suggestion 代码块写建议改法,作者点一下「Apply」就能直接采纳,不用手动改。这是最高效的 review 方式。

review 礼仪:对事不对人,提建议而非命令,说清「为什么」要改。第 05 章团队工作流有完整的 review checklist。

四种审查动作

review 完成时,你要做一个明确的表态

动作含义何时用
Comment 仅评论,不表态 有疑问但不阻塞合并
Approve 同意合并 代码没问题,可以进主干
Request changes 要求修改 有问题必须改才能合并
(不操作) 静默同意 没意见就不点,让有权限的人合并

被 request changes 后,作者改完代码会自动清除「需修改」状态,重新等待 review。

合并策略

PR 通过审查后,有三种方式把它并进主干

图:三种合并策略对历史的影响

Merge commit

最传统的方式:保留分支的所有提交,额外加一个合并节点

优点

  • 完整保留分支历史和工作过程
  • 能看出「哪些提交是一起做的」
  • 大项目、多人协作的默认选择

缺点

  • 历史图会分叉再合并,复杂
  • 主干混入大量小提交
  • feature 分支上的「wip」「修 typo」也进历史

适合:大型长期项目、想完整保留协作轨迹的团队。

Squash 合并

把分支上的所有提交压成一个,再并进主干

优点

  • 主干历史是干净的直线
  • 每个 PR 对应主干一个提交
  • 回退整个功能很容易

缺点

  • 丢失分支内的细粒度提交历史
  • 原始 commit hash 改变
学生项目、小团队首选 Squash。它让主干历史像目录一样清晰:「这次加了登录」「那次修了 bug」,而不用看一堆「wip」「再改一下」。
本教程网站自己的提交历史就是 Squash 风格。

Rebase 合并

把分支提交「搬到」主干末端,线性排列,不加合并节点

优点

  • 历史是干净的直线
  • 保留分支上的每个提交
  • 适合提交本身就有意义的项目

缺点

  • 历史图虽线性但提交多
  • 提交顺序可能和实际工作顺序不符
怎么选的速记:要干净直线选 Squash,要保留每步且线性选 Rebase,要完整轨迹选 Merge commit。个人/学生项目 → Squash;开源大项目 → Merge 或 Rebase。

合并冲突

当你的分支和主干改了同一处代码,Git 无法自动合并

<<<<<<< HEAD
这是你分支上的版本
=======
这是主干上的版本
>>>>>>> main

冲突标记三段

  • <<<<<<< 到 =======:你的改动(HEAD)
  • ======= 到 >>>>>>>:对方的改动
  • 你需要手动决定保留哪个,或合并两者
冲突不是错误,是 Git 在请你做判断。它把两种版本都摆出来,让你决定最终长什么样。删掉冲突标记,保留想要的代码即可。

网页端解冲突

小冲突直接在 GitHub 网页上改,不用开终端

何时能用

点 Merge pull request 时,如果 GitHub 检测到冲突且冲突文件不多,会出现 Resolve conflicts 按钮。

操作步骤

  1. 点 Resolve conflicts
  2. 网页编辑器显示带冲突标记的文件
  3. 手动删掉 <<< / === / >>> 标记,保留正确代码
  4. 点 Mark as resolved → Commit merge
  5. 回到 PR 页面继续合并
适合场景:改了几行 README、调了个文案这种小冲突。涉及多文件、逻辑改动大的冲突,还是用命令行更靠谱。

命令行解冲突

复杂冲突在本地解决,掌控力最强

# 1. 把最新主干拉进你的分支
$ git fetch origin
$ git merge origin/main

# 2. Git 提示冲突文件
# Auto-merging app.js
# CONFLICT (content): Merge conflict in app.js

# 3. 打开 app.js,手动解决冲突标记

# 4. 标记已解决
$ git add app.js

# 5. 完成合并
$ git commit
$ git push
解冲突的核心原则:理解两边都想达到什么目的,而不是简单选一个。常常最终代码是两种方案的综合——既要 A 的功能,也要 B 的修复。

放弃合并

冲突太复杂、改到一半想重来——随时可以退出

# 合并进行中,想放弃
$ git merge --abort

# 回到合并前的干净状态,仿佛什么都没发生

什么时候用

  • 冲突太多,一次性理不清
  • 改到一半发现方向错了
  • 想先去请教别人再回来处理
放心地 abort。Git 的合并是安全的,--abort 不会丢失你之前的 commit,只是取消这次没完成的合并操作。你的工作都在。

合并之后

PR 合并了,善后工作别忘

# 1. 切回主干并拉取最新(含你刚合并的改动)
$ git checkout main
$ git pull

# 2. 删除已合并的本地功能分支
$ git branch -d feature/add-login

# 3. 删除远程的功能分支(GitHub 合并时通常自动提示删)
$ git push origin --delete feature/add-login
为什么要删分支:已合并的功能分支完成了使命,留着会让分支列表越来越乱。-d(小写)是安全删除——只允许删已合并的分支,未合并的要用 -D 强删。

好 PR 的习惯

让 reviewer 爱上你的 PR

小而专一

一个 PR 只解决一件事。别把「加登录」和「改 README」塞进同一个 PR。小 PR 审得快,合并快。

标题就是结论

「添加邮箱登录功能」比「update」好一万倍。标题要让别人扫一眼 PR 列表就知道干了啥。

描述说清 why

代码已经展示了「怎么改」,描述要解释「为什么改」。reviewer 需要上下文。

自测再提交

开 PR 前自己先跑通。把未完成的、跑不起来的代码丢给别人审,是对别人时间的不尊重。

排错:PR 不见分支

开了 PR,但 Files changed 显示「There isn't anything to compare」

原因

你的功能分支和主干没有差异——可能是你忘记在功能分支上 commit,或 commit 后忘记 push。

检查清单

# 确认在功能分支
$ git branch

# 确认有新提交
$ git log main..HEAD

# 确认推到了远程
$ git status

git status 显示 ahead of origin 说明还没 push。

动手试一试

完整走一遍 PR 全流程

你的任务

任务:在自己的仓库里,通过 PR 修改 README。

要求:

  1. 开 feature 分支 docs/improve-readme
  2. 在 README 里加一段「项目结构」说明
  3. commit 并 push 该分支
  4. 在 GitHub 开 PR,写清楚描述
  5. 自己 review 一遍,选 Squash 合并
  6. 合并后清理本地和远程分支

提示

  1. 一个人也能走完 PR——你就是作者也是 reviewer
  2. 合并后记得 git pull 把合并结果拉回本地 main
第 03 章

小结

你已经掌握 GitHub 协作的核心——PR 全流程

本章要点

  • feature branch 工作法:开分支 → commit → push → 开 PR
  • PR 四标签:Conversation / Commits / Files changed / Checks
  • 三种合并策略:Merge commit(完整)/ Squash(干净)/ Rebase(线性)
  • 冲突可在网页解(小)或命令行解(大),随时可 abort
  • 合并后清理分支,保持仓库整洁
下一步:第 04 章 Issues 与 Projects —— 学会用 issue 跟踪任务、用看板管理进度,让 PR 不再是无序的。