这篇讲怎么按目录自动指定谁来评审。看完你可以写一份 CODEOWNERS 文件,按路径写明每段代码由谁负责,打开 启用 CODEOWNERS 之后,新建拉取请求就会按改动路径把这些人加为评审人。
下文以仓库 xlliu/test-pr-example 对照。这个仓库已经打开 需要代码评审 和 需要所有评审人通过(被指派的人都批准才能合并),但 启用 CODEOWNERS 还是关的。它设置了两位默认评审人,也就是在仓库设置里固定指定、每条新拉取请求都会带上的人:一位显示名是 Angus、登录名是 xlliu,另一位是 admin。
1. 现在这条拉取请求是怎么来的
还开着的 #9 标题是 docs: 说明空购物车不计算折扣,从 feature/link-empty-cart 合向 main,作者是 Angus,正文写了 Closes #4,只改了 1 个文件:docs/empty-cart-note.md(+3 -0)。
评审人是 Angus 和 admin,两人都是 待评审。合并卡片写着「此拉取请求尚未准备好合并」,下面是「暂无状态检查」和「仍有评审未完成,需等待评审通过后才能合并」,所以 合并拉取请求 是灰的。

所以这两位来自仓库设置里的默认评审人,不是按文件路径算出来的;main 上目前也没有 .github/CODEOWNERS、根目录 CODEOWNERS 或 docs/CODEOWNERS。
2. CODEOWNERS 实际会做什么
CODEOWNERS 只在新建拉取请求时算一次,而且仓库要先打开 需要代码评审;创建之后再往源分支推提交,不会按新文件补评审人。
指派时会做两件事,再去重:
- 放上设置里的默认评审人。
- 启用 CODEOWNERS 开着时,再读目标分支上的文件,按这次改动的路径追加人。
所以默认名单里已经有的人,文件里再写一次也不会变成两行评审。这个仓库的默认名单已经是 xlliu 和 admin 两个人。文件里如果也只写这两位,打开开关后新建的拉取请求,评审人还是这两位。
| 能做 | 做不到 |
|---|---|
| 创建拉取请求时,把默认评审人和路径匹配到的人并在一起 | 拉取请求创建后再推送,不会重新算、也不会补人 |
只认 @登录名 |
团队名(中间有 /)和邮箱会被跳过 |
| 配合 需要所有评审人通过,被指派的人都批准才能合并 | 只开 CODEOWNERS、不开这一项时,CODEOWNERS 自己不拦合并 |
作者自己被指派时,不再给自己发「请求评审」通知。其他评审人会收到。
3. 文件怎么写
CODEOWNERS 文件可以放在三个位置,按下面的顺序找,用找到的第一个:
.github/CODEOWNERS- 仓库根目录的
CODEOWNERS docs/CODEOWNERS
读的是拉取请求目标分支上的文件。要合进 main,就得先把文件合进 main。只改在自己的分支上,对这条拉取请求不起作用。
下面这份是给这个仓库准备的示例,还没有提交。登录名用 @xlliu,不要写显示名 @Angus。
# 没被后面规则盖住的文件
* @xlliu
# 根目录下的 service 目录
/service/ @admin
# 任意目录的 SQL,写在后面,盖住前面的规则
*.sql @xlliu @admin
# 开头和行尾的注释会被拿掉,空行忽略。路径和人之间用空格分开。
| 写法 | 匹配 |
|---|---|
* |
所有文件。单独一行时做兜底 |
/service/ |
从仓库根目录算的 service 目录,以及里面的文件。开头的 / 表示锚定到根 |
*.sql |
任意一层目录里、以 .sql 结尾的文件 |
/docs/**/*.md |
docs 下任意层级的 .md |
? |
一个不是斜杠的字符。v?.txt 能对上 v1.txt |
同一个文件命中多行时,用最后一行。
docs/empty-cart-note.md只命中*,人是@xlliu。service/Foo.java先命中*,再命中/service/,留下@admin。service/db/init.sql三行都命中,最后一行是*.sql,留下@xlliu和@admin。
以 ! 开头不会当成排除。方括号不会当成字符集,只按字面量里的括号去匹配。
4. 开关在哪
入口是 仓库设置 → 拉取请求,地址带 ?tab=settings§ion=pullrequests。

这次看到的是:
| 项 | 状态 |
|---|---|
| 需要代码评审 | 开。说明是「合并前必须经过代码评审,开启后针对所有分支都生效。」 |
| 需要的评审人数 | 1 |
| 评审人 | 已选择 2 个:Angus、admin |
| 需要解决所有评论 | 关 |
| 需要所有评审人通过 | 开。说明是「合并前所有指派的评审人都必须通过评审。」 |
| 启用 CODEOWNERS | 关。说明是「根据 .github/CODEOWNERS 文件按代码路径自动指派评审人。」 |
| 评审通过后自动合并 | 开。还要这条拉取请求自己也打开自动合并才会合。#9 的合并按钮仍是灰的 |
| 自动指派评审人(负载均衡) | 关 |
| 评审超时提醒(小时) | 0,表示关闭 |
打开 需要代码评审 时,人数至少为 1,并且至少选一位评审人,否则保存会提示「启用代码评审时,必须设置评审人数并选择至少一个评审人」。保存成功的提示是「拉取请求设置已保存」。页面上已经是这些值时,打开设置只是查看,不必再保存。
负载均衡开着、而且候选人数多于 需要的评审人数 时,只留下未完成评审最少的那几位,人数等于所需人数。代码所有者可能被挑掉。这个仓库它是关的,两位默认评审人都会留下。
需要所有评审人通过 看的是已经指派的每一个人,包括默认评审人和 CODEOWNERS 追加的人。人数设成 1 时,如果没开「所有人通过」,一位批准就够人数。这个仓库开了「所有人通过」,所以 #9 要等 Angus 和 admin 都批准。页面上现在的红字是「仍有评审未完成,需等待评审通过后才能合并」。合并请求若仍然发出去,接口返回的是「需要所有评审人通过后才能合并,当前仍有「1」个评审人未通过」。#9 还开着,等两位评审人都通过再合并。
5. 和分支保护
仓库设置里的 需要代码评审 管的是拉取请求能不能合,并不阻止直接往 main 推。这个仓库的 设置 → 分支保护 写着「尚未设置分支保护规则」;要拦住直接推送、让改动必须走拉取请求,再点 添加规则,给 main 打开需要评审,并对管理员同样生效。做法见文末的分支保护一文。
合并时如果目标分支的保护规则里填了评审人数,用保护规则上的人数;没填才用仓库设置里的 需要的评审人数。需要所有评审人通过 仍看仓库设置。
6. 没有按路径指派时
| 你看到的 | 原因 | 怎么处理 |
|---|---|---|
| 只有默认评审人 | 启用 CODEOWNERS 关着,或目标分支上三个位置都没有文件。#9 就是前一种 | 把文件合进目标分支,打开开关,保存后再新建一条拉取请求 |
| 文件里写了人,评审列表里没有 | @ 后面不是登录名,或写了团队名、邮箱 |
改成 @xlliu 这种登录名。显示名 Angus 对不上 |
| 指派的不是预想的那一行 | 同一个文件命中了多行,生效的是最后一行 | 把更具体的规则放到后面,或把几个人写在同一行 |
| 创建后又改了别的目录,新人没出现 | 只在创建时算一次 | 由作者或管理员手工把这个人加为评审人 |
| 人还没批准就被合并了 | 需要所有评审人通过 关着,而且批准人数已经达到所需人数 | 打开「所有人通过」。这个仓库已经开着 |
常见问题
CODEOWNERS 里的人没有仓库权限会怎样?
他仍会被加进评审人。要打开拉取请求并提交评审,需要这个仓库的读取权限。
社区版能用吗?
能。需要代码评审、启用 CODEOWNERS 和 需要所有评审人通过 都在仓库 设置 → 拉取请求 里。
两个人里任意一个批准就行,能做到吗?
把两个人写在同一行,关掉 需要所有评审人通过,需要的评审人数 填 1,并保持负载均衡关闭。这样一位批准就够人数。代价是:任何一位已指派的人批准都算数,不限是不是代码所有者。这个仓库现在开着「所有人通过」,所以不是这种算法。
小结
xlliu/test-pr-example 没有 CODEOWNERS 文件,启用 CODEOWNERS 是关的。#9 的评审人 Angus 和 admin 来自默认名单。两人都还是待评审,合并按钮不可用。
要按目录加人:把 .github/CODEOWNERS 合进目标分支,规则用 @登录名,同一文件以最后一行为准;然后打开 启用 CODEOWNERS 并保存。新建的拉取请求会把文件里匹配到的人和默认评审人并在一起。这个仓库已经打开 需要所有评审人通过,负载均衡是关的,被指派的人都要批准。
结论: 默认评审人在创建拉取请求时就会指派。CODEOWNERS 要文件先在目标分支上,并且打开启用开关,才会按路径把登录名并进去。需要所有评审人通过开着时,这些人都批准才能合并。