2 minute read

“空谈廉价,拿出代码。” — Linus Torvalds

你能在 60 秒内找出“该你审、已阻塞、快过期”的所有 PR 吗?

别把 PR 列表当成一张表:终端里至少有三条可用的查询路径。

“搜 PR,不就是 gh pr list 加个 --search 吗?”

这句话只对了一半。你当然能这样做;但当你不知道 PR 在哪个仓库、需要查它改了什么文件,或者网络不可用时,换的不是参数,而是查询路径和数据来源。

下面这套分法能让你在一分钟内写出“该我审”“被阻塞”“久未更新”的查询,并知道什么时候该从 gh 切到本地 git。

先跑一条最常用的。把 OWNER/REPO 换成目标仓库:

gh pr list --repo OWNER/REPO --state open \
  --search 'review-requested:@me' \
  --json number,title,updatedAt,url

它找的是明确请求你 review 的开放 PR。再加上团队已有的标签和日期约定,就能把待办从“列表里好像有几条”变成可执行的筛选。

你要找什么 可直接追加的条件 它表达的事实
该你审 --search 'review-requested:@me' 你被请求审查
已阻塞 --label blocked 仓库用 blocked 标签标记阻塞
快过期 --search 'updated:<YYYY-MM-DD' 自团队定义的陈旧阈值以来没有更新

“快过期”不是 GitHub 的内建状态,而是团队对“陈旧”的业务定义。 先按团队的陈旧策略计算 YYYY-MM-DD,再运行查询;标签名和是否要排除 draft,也应当按你们的规则调整。

这张表就是最值得截图的部分:把 PR 工作流里的自然语言,翻译成可验证的筛选条件。

一个仓库内,先用 gh pr list

假设你改过一个依赖约束,现在只记得关键词和大概文件名。网页翻页很磨人;终端里先从仓库边界开始:

gh pr list --repo OWNER/REPO --author @me --state all \
  --search 'sftp pyproject'

--author @me 表示当前登录用户;--state all 很关键。gh pr list 默认只列开放 PR,漏掉已合并 PR 是这里最常见的误判。

如果标题和描述里没留下关键词,先换成文件名或更稳定的术语:

gh pr list --repo OWNER/REPO --author @me --state all \
  --search 'pyproject.toml'

还找不到,再把候选拉成 JSON,在本地按标题过滤:

gh pr list --repo OWNER/REPO --author @me --state all \
  --limit 100 --json title,number,url \
  --jq '.[] | select(.title | test("sftp"; "i"))'

这里有个容易混淆的边界:--search 使用 GitHub 的 PR 搜索语法;它不是“搜索所有改动过的文件内容”。因此,拿文件路径或代码细节做唯一检索条件,结果未必完整。

日期、标签、审查人和目标分支都可以叠加:

# 某段时间内创建,并且带关键词
gh pr list --repo OWNER/REPO --author @me --state all \
  --search 'created:YYYY-MM-DD..YYYY-MM-DD sftp'

# 团队陈旧阈值之后更新过
gh pr list --repo OWNER/REPO --state all \
  --search 'updated:>YYYY-MM-DD'

# 同时具备多个标签
gh pr list --repo OWNER/REPO --label bug --label hotfix

# 只看合入某分支的 PR
gh pr list --repo OWNER/REPO --base main

多个 --label 是叠加条件:要同时命中。审查维度也可以写进搜索:reviewed-by:USERNAME 查某人审过的 PR,review-requested:@me 查需要你处理的 PR。

找到编号之后,终端不必退出工作流:

gh pr diff PR_NUMBER --repo OWNER/REPO
gh pr view PR_NUMBER --repo OWNER/REPO
gh pr checkout PR_NUMBER --repo OWNER/REPO
gh pr review PR_NUMBER --approve
gh pr review PR_NUMBER --comment --body 'LGTM'
gh pr review PR_NUMBER --request-changes --body '请检查版本约束是否符合预期'
gh pr view PR_NUMBER --web --repo OWNER/REPO

不知道仓库在哪?换成 gh search prs

gh pr list 的强项是“这个仓库里的 PR”。当记忆只剩下“我曾在某个仓库改过这个依赖”,应该换成跨仓库搜索:

gh search prs 'sftp pyproject' --owner OWNER --author @me
gh search prs 'sftp' --owner OWNER --language python
gh search prs 'sftp' --owner OWNER --merged

原始笔记里把两者说成“搜索深度不同”。更准确的说法是:它们首先差在作用域和查询接口。gh search prs 面向 GitHub 的跨仓库 PR 搜索;gh pr list 面向一个仓库的 PR 列表,并可附带搜索条件。关键词具体匹配哪些可搜索字段,应以 GitHub 当前的搜索语法为准,不要把它当作代码 diff 搜索器。

你知道什么 优先工具 为什么
仓库确定 gh pr list 仓库边界已知,筛选直接
仓库不确定 gh search prs 能跨仓库收集候选
已知 PR 编号且本地有历史 git log --grep 不必发网络请求
要看评论、审查或 CI gh pr view 这些是 PR 元数据,不在 Git 提交里

PR 搜索至少有三条查询路径:仓库内 PR 列表、跨仓库 PR 搜索、本地 Git 提交历史。

我的立场是:不要急着做一个“万能 PR 搜索命令”。最有价值的快捷方式,是把“我现在知道什么”编码进去。反方的理由也成立:固定流程、固定仓库的团队,用一个统一 alias 可以减少输入和培训成本。只是 alias 一旦试图猜测仓库、状态、时间范围和责任人,省下的击键往往会以漏结果的形式还回来。

常用且稳定的默认条件,可以做成 alias:

gh alias set my-prs 'pr list --author @me --state all'
gh my-prs --repo OWNER/REPO --search 'sftp'

如果某个仓库确实是你的固定工作台,也可以把 --repo OWNER/REPO 写进 alias;代价是它会把搜索范围悄悄锁死。

文件级问题,gh api graphql 是检查器,不是魔法搜索

当你已经拿到一批 PR,却要确认各自改过哪些文件,GraphQL 能直接取回文件路径:

gh api graphql -f query='
{
  repository(owner: "OWNER", name: "REPO") {
    pullRequests(last: 10, states: [MERGED], orderBy: {field: UPDATED_AT, direction: DESC}) {
      nodes {
        number
        title
        mergedAt
        files(first: 5) {
          nodes { path }
        }
      }
    }
  }
}'

它会检查最近一批已合并 PR 的前几个文件,而不是在服务端替你搜索“所有改过某文件的 PR”。结果多于 last: 10 或每个 PR 多于 first: 5 个文件时,还要处理分页。这个限制很重要:GraphQL 给你的是可组合的数据,不会自动替你定义“查全”。

若想交互挑选 PR,fzf 很顺手:

gh pr list --repo OWNER/REPO --author @me --state all --limit 50 \
  --json number,title --jq '.[] | "\(.number)\t\(.title)"' \
  | fzf --delimiter=$'\t' --with-nth=2.. \
        --preview 'gh pr view {1} --repo OWNER/REPO' \
  | cut -f1 \
  | xargs -I{} gh pr view {} --web --repo OWNER/REPO

网络断了,Git 历史还有一条路

已知 PR 编号时,本地仓库可以直接查 merge commit:

git log --grep='#PR_NUMBER' -n 5
git log --grep='#PR_NUMBER' -n 1 -p
git log --grep='#PR_NUMBER' --merges -n 5
git log --grep='sftp' --author='NAME' --oneline -n 10
git log --grep='#PR_NUMBER' -n 1 --stat

很多通过 merge commit 合并的 PR,会把 PR 编号写进提交信息,因此这招不需要 GitHub API。它搜的是本地提交消息,不是 PR 标题、评论、标签或 CI 状态。

也别把它当成必中的离线后门:squash merge、rebase merge、自定义提交信息,或者本地 clone 没有完整历史,都可能让 PR 编号消失。它适合“我有编号,想快速确认改了什么”;没有编号时,gh 的 PR 元数据通常更合适。

如果你的仓库把“阻塞”放在标签里、把“陈旧”留给日期判断,今天就按团队策略算出一次阈值,跑一次开头那张表里的三条查询。然后问自己一个更难的问题:你们的 PR 状态,到底有没有被写进机器能检索的字段,还是只存在某个人的记忆里?

Updated: