如何用 gh api 不克隆就读取 GitHub 仓库的代码
返回博客
文章githubdev toolshow toagente de programaçãoverboo code

如何用 gh api 不克隆就读取 GitHub 仓库的代码

Mafra2026年10月22日阅读约 6 分钟

自 2026-09-09 起,本博客有 24 轮生产流程用 gh api 查询 GitHub,其中 16 篇是 Verboo Code 教程,全部直接读取 verbeux-ai/code 仓库写成,一次 git clone 都没有。每篇教程需要从一个有 3,115 个条目的仓库里读五到十个文件。为了读十个文件把整个仓库克隆下来是浪费,而在我们运行的环境(带智能体沙箱的 VPS)里,克隆也不总是可行。

本文展示我们只通过 API 来列出、筛选、读取仓库并固定版本时用到的 gh(GitHub CLI)命令。所有命令都于 2026-10-22 用 gh 2.100.0 实际运行过。API 的限制来自 GitHub 官方文档,同日查阅。

四步流程图:用 git/trees?recursive=1 列出整个仓库,用 --jq 筛选目录,用 Accept raw 请求头读取原始文件,用 ?ref 固定版本;如果树被截断,不加 recursive 逐个子树读取
四个步骤,从仓库全貌到正确版本的文件,并附上仓库过大时的分支。

怎样不克隆就列出 GitHub 仓库里的所有文件?

用 git/trees 端点加 recursive 参数。一次调用就能返回该分支上每个文件的路径和大小:

gh api "repos/verbeux-ai/code/git/trees/main?recursive=1" \
  --jq '{truncated, total: (.tree|length), files: ([.tree[]|select(.type=="blob")]|length)}'

2026-10-22 的输出:

{"files":2737,"total":3115,"truncated":false}

这 3,115 个条目包括目录(type: "tree")和文件(type: "blob")。关键字段是 truncated:如果为 true,列表就不完整。GitHub 文档规定递归响应的上限是 100,000 个条目、7 MB。超过之后,就去掉 recursive 请求树,用每个 tree 条目的 sha 逐个子目录往下读。

文档里的一个细节:recursive 填任何值都会开启递归,包括 0 和 false。要关闭,就不要带这个参数。

怎样只看某个目录下的文件?

用 --jq 按路径前缀筛选同一棵树。这样打开之前就能看到每个文件的大小:

gh api "repos/verbeux-ai/code/git/trees/main?recursive=1" \
  --jq '.tree[] | select(.type=="blob" and (.path|startswith("src/commands/add-dir/"))) | "\(.size)\t\(.path)"'
4825	src/commands/add-dir/add-dir.tsx
260	src/commands/add-dir/index.ts
3207	src/commands/add-dir/validation.ts

如果只看一个目录,contents 端点也能列出,不递归:

gh api repos/verbeux-ai/code/contents/src/commands/add-dir --jq '.[] | "\(.type)\t\(.name)"'

区别在上限:文档说明 contents 每个目录最多列出 1,000 个文件,超过就要改用 trees API。要在整个仓库里按文件名找文件,递归树是最短的路。

我们写 /add-dir 教程 时用的就是这一步:树里列出了这个命令的三个文件,再按其他前缀筛选,又找到了它涉及的权限和配置文件。

怎样在终端里读取文件内容?

向 contents 端点请求文件,并带上原始格式的请求头。返回的就是文件文本,没有 JSON,也没有 base64:

gh api repos/verbeux-ai/code/contents/src/commands/add-dir/index.ts \
  -H "Accept: application/vnd.github.raw"
import type { Command } from '../../commands.js'

const addDir = {
  type: 'local-jsx',
  name: 'add-dir',
  description: 'Add a new working directory',
  argumentHint: '<path>',
  load: () => import('./add-dir.js'),
} satisfies Command

要在大文件里找某一行,把输出交给 grep -n,它会同时给出行号:

gh api repos/verbeux-ai/code/contents/src/commands/add-dir/add-dir.tsx \
  -H "Accept: application/vnd.github.raw" | grep -n "import"

陷阱:--jq 不能和原始格式一起用

--jq 需要 JSON。带上 raw 请求头后,响应是纯文本,gh 会报这个错:

invalid character 'i' looking for beginning of value

如果想不用管道就筛选(当环境禁止 | 时很有用,有些智能体沙箱就是这样),去掉请求头,在 jq 里直接解码 base64:

gh api repos/verbeux-ai/code/contents/src/commands/add-dir/add-dir.tsx \
  --jq '.content | @base64d | split("\n") | to_entries[] | select(.value|test("import")) | "\(.key+1): \(.value)"'

这只适用于 1 MB 以内的文件。超过之后 content 字段为空,见下表。

通过 GitHub API 读取文件的大小上限是多少?

1 MB 以内一切正常。1 到 100 MB 之间,只有原始格式能返回内容。超过 100 MB,这个端点不提供文件。数字来自 contents 和 trees 端点的官方文档:

情况会发生什么怎么做
文件不超过 1 MB所有格式都可用用 raw,或 --jq 加 @base64d
文件 1 到 100 MBcontent 为空,encoding 为 "none"使用 Accept: application/vnd.github.raw
文件超过 100 MB端点不支持克隆,或下载 release
目录超过 1,000 个文件contents 会截断列表使用 git/trees
递归树超过 100,000 个条目或 7 MBtruncated: true不加 recursive,逐个子目录读取

在 verbeux-ai/code 里,最大的文件是 265,586 字节,所以全部落在表格第一行。

怎样读取某个特定版本的文件?

加上 ?ref=,后面跟标签、分支或提交哈希。这样才能保证教程描述的是读者安装的版本,而不是之后 main 上的改动:

gh api repos/verbeux-ai/code/releases/latest --jq .tag_name
gh api "repos/verbeux-ai/code/contents/package.json?ref=v0.15.38" \
  -H "Accept: application/vnd.github.raw" | grep -m1 '"version"'
v0.15.38
  "version": "0.15.38",

树也一样:在 git/trees/v0.15.38?recursive=1 里把 main 换成标签即可。想知道某个文件最后一次修改的时间,commits 端点可以按路径筛选:

gh api "repos/verbeux-ai/code/commits?path=src/commands/add-dir/add-dir.tsx&per_page=3" \
  --jq '.[] | "\(.sha[0:7]) \(.commit.author.date[0:10])"'

为什么不用 gh search code 找文件?

因为代码搜索有自己的索引规则,而树没有,而且有时会不报错地返回空结果。同一天,搜索代码里确实存在的词,在 verbeux-ai/code 上返回为空,在 openai/codex 上却有结果:

gh search code "addDir" --repo verbeux-ai/code --limit 3
gh search code "useState" --repo openai/codex --limit 2 --json path --jq '.[].path'

GitHub 文档列出了这些限制:只索引默认分支,只索引小于 384 KB 的文件,只索引过去一年有活动的仓库,fork 只有星标多于上游时才会被收录。我们不知道是哪一条影响了我们的仓库。实际结论是:搜索为空并不能证明这个词不存在。递归树会列出全部内容,中间没有索引。

编程智能体能自己完成这些吗?

能,我们的教程就是这样写出来的。智能体先跑出树,按路径和大小挑文件,逐个以原始格式完整读取,再在正文里注明文件和版本。一篇典型教程要完整读五到十个文件,有的长达几百行。读取成本就在这里累积:在 Verboo Code 上 token 不限量,所以为了核对一个参数读完整个文件,和只读你以为够用的那一段,花费一样。

喜欢这篇文章吗?
把知识分享给你的朋友。
// 继续阅读

相关文章