1. 这篇文章想讲什么
今天我第一次完整走了一遍比较标准的 GitHub 协作流程:
- 把一个历史分支重新命名成更清晰的功能分支
- 从功能分支发起 PR
- 处理 Cloudflare Pages 的构建失败
- 等检查通过后,再把代码合并进
master
这一套流程做完之后,我发现很多人第一次接触 PR 时,真正困惑的通常不是“按钮在哪里”,而是三个更底层的问题:
- PR 到底是什么
- 为什么不能直接把代码推到
master - PR 在原理上到底做了什么
所以这篇文章不只记录操作步骤,更想把背后的逻辑讲清楚。
2. 什么是 PR
PR 是 Pull Request,在 GitHub 里,它的意思可以直接理解成:
我在一个分支上完成了一组改动,现在正式请求把这些改动合并到另一个分支。
比如这次我的流程里,源分支是:
feature/runtime-mobile-performance
目标分支是:
master
那么这次 PR 的含义就是:
我请求把
feature/runtime-mobile-performance里的改动,合并到master。
注意,PR 不是“把代码上传到 GitHub”。
代码在 git push 的时候就已经上传了。
PR 做的是另一件事:
把“上传的代码”变成“等待审核和合并的改动提案”。
也就是说,push 解决的是“代码到了远端没有”,PR 解决的是“这组代码要不要进主分支,以及怎么进”。
3. 为什么要做 PR,而不是直接改 master
如果只是自己写一个很小的项目,直接在 master 上提交并不是技术上不可以。
但只要项目开始进入“持续维护”和“持续发布”的状态,PR 的价值就会迅速变大。
最核心的原因有四个。
3.1 PR 让改动有边界
一个功能、一组修复、一次重构,都应该有比较清晰的改动范围。
比如这次分支名从 optimization 改成 feature/runtime-mobile-performance,本质上就是在表达:
这个分支负责的是“运行时重构 + 移动端性能优化”这一组工作,而不是一些模糊的杂项修改。
当改动是有边界的,后续代码审查、回滚、问题定位都会简单很多。
3.2 PR 让合并动作变成可审查事件
没有 PR 的情况下,改动会直接进入 master。
有了 PR 之后,合并之前会先出现一个明确的检查窗口:
- 这次改了什么
- 改动文件有哪些
- 检查是否通过
- 有没有明显风险
就算是单人开发,这个窗口也有价值。因为它把“写代码”和“决定上线”拆成了两个动作。
3.3 PR 让自动化检查有落点
这次最直观的例子就是 Cloudflare Pages。
我在本地构建是通过的,但 PR 检查里,Cloudflare 一开始失败了。失败的原因不是页面逻辑本身,而是:
package.json和package-lock.json不同步,导致 Cloudflare 在干净环境里执行npm ci时报错。
如果没有 PR,这个问题很可能会等到代码已经进入 master 之后才暴露。
但因为有 PR,问题被提前拦在了合并前。
3.4 PR 让发布分支保持稳定
今天最终定下来的策略是:
- 日常开发放在
feature/*分支 - 合并后进入
master - Cloudflare 以
master作为生产发布分支
这个策略的重点不是“多开一个分支”,而是:
只有经过检查并完成合并的代码,才进入生产发布链路。
这比“所有开发都直接堆进 master”更稳。
4. PR 的原理到底是什么
很多人第一次用 PR,会误以为 PR 是 GitHub 特有的“魔法操作”。
其实不是。
PR 的底层建立在 Git 原本就有的两件事上:
- 分支
- 提交差异比较
GitHub 只是把这两件事做成了一个可视化工作流。
4.1 分支本质上是提交历史的指针
在 Git 里,一个分支名,本质上只是“指向某个提交”的引用。
比如:
master指向线上稳定代码所在的提交feature/runtime-mobile-performance指向功能开发中的最新提交
当这两个分支各自继续提交时,它们就会沿着不同的提交链向前移动。
PR 做的第一件事,就是把这两条提交链拿出来比较。
4.2 PR 比较的是“源分支相对目标分支多出来的提交和文件改动”
当我在 GitHub 上选择:
base: mastercompare: feature/runtime-mobile-performance
GitHub 实际上是在问:
相比
master,feature/runtime-mobile-performance多了哪些提交,最终会带来哪些文件差异?
这些差异会被展示成:
- commit 列表
- file diff
- 可否自动合并
- 是否存在冲突
所以 PR 页面本质上是一份“合并影响报告”。
4.3 “Able to merge” 的意思是什么
PR 页面里显示 Able to merge,并不表示“代码一定没问题”。
它只表示一件事:
Git 能自动把这两个分支的文本改动合并起来,没有出现需要人工处理的冲突。
它解决的是“能不能合”,不是“应不应该合”。
是否应该合,还要看:
- 自动化检查是否通过
- 改动是否符合预期
- 发布风险是否可接受
4.4 检查通过后,合并本质上是在更新目标分支指针
当 PR 被 merge 之后,结果并不是 GitHub 把文件一行一行“搬过去”。
更准确地说,是:
GitHub 根据你选择的合并策略,生成新的提交关系,然后把
master指向新的提交。
如果用的是 Squash and merge,它会把源分支上的一组提交压成一个新的提交,再把这个新提交放进 master。
这样做的好处是,master 的历史会更干净。
尤其是功能分支上有很多临时提交时,压缩后更适合作为长期维护的主历史。
5. 结合今天这次操作,PR 实际是怎么落地的
今天这次流程,大致是这样:
5.1 先把原分支改成更清晰的名字
原来的分支叫 optimization。
这个名字的问题是太泛,别人以后看到时很难知道它到底改了什么。
所以先把它改成:
feature/runtime-mobile-performance
这样分支名本身就带上了改动边界。
5.2 把 origin/master 合进功能分支
这一步的目的,不是把功能分支提前合并掉,而是:
先把目标分支的最新内容同步到当前分支,尽量在发 PR 前解决潜在冲突。
这样 PR 页面的 diff 会更干净,后面合并也更稳。
5.3 推送分支,创建 PR
推送之后,GitHub 就可以基于这两个分支生成比较页面。
这时 PR 正式成立,但还没有完成合并。
5.4 让自动化检查先跑完
这次 Cloudflare Pages 一开始失败了。
从日志里定位下来,问题不是 Astro 页面构建逻辑,而是:
- Cloudflare 使用
npm ci npm ci依赖严格一致的 lockfile- 当前 lockfile 不完整,导致依赖解析失败
也就是说,这次 PR 发挥的作用不是“展示按钮”,而是提前暴露了发布链路里的真实问题。
5.5 修复检查,再合并
等 Cloudflare 检查变绿之后,PR 才真正进入可合并状态。
这时再执行 Squash and merge,逻辑就完整了:
- 功能在分支里开发
- 风险在 PR 里暴露
- 发布前检查通过
- 通过后再进入
master
这就是一个规范的工程闭环。
6. PR 和发布流程是什么关系
很多人把 PR 理解成“代码协作工具”,这是对的,但还不够完整。
从工程角度看,PR 更重要的作用是:
把开发分支和发布分支之间,插入一道明确的质量闸门。
在今天这个项目里,这道闸门前面是:
feature/runtime-mobile-performance
这道闸门后面是:
master- Cloudflare 生产部署
也就是说,PR 不只是“方便看 diff”,它还是开发流和发布流之间的连接器。
如果没有 PR,这两者之间就几乎是直通的。
一旦有了 PR,部署系统、构建系统、代码审查系统,就都能挂在这里一起起作用。
7. 我现在对 PR 的理解
做完今天这一整套之后,我觉得 PR 最准确的理解不是“GitHub 上的一个页面”,而是:
一次正式的合并申请,一份改动影响报告,以及一道进入发布分支前的检查门。
它把原本混在一起的几件事拆开了:
- 开发,在功能分支里进行
- 比较,在 PR 页面里完成
- 检查,在自动化系统里执行
- 合并,在确认无误后发生
- 发布,在目标分支更新后触发
这也是为什么 PR 会成为现代软件工程里非常常见、非常稳定的一种实践。
它不是为了把流程变复杂,而是为了让复杂性有地方被看见、有地方被拦住。
8. 结语
如果只从表面上看,PR 似乎只是 GitHub 上一个绿色按钮前后多出来的一层页面。
但只要真正走过一遍,就会发现它解决的是工程里非常核心的问题:
- 改动如何被清晰表达
- 风险如何在合并前暴露
- 代码如何以更稳定的方式进入发布链路
今天这次操作,本质上不是“学会了怎么点 PR 按钮”,而是第一次把分支开发、自动化检查、合并策略和生产发布这几件事连成了一条完整链路。
这比单纯记住几个 Git 命令更重要。