Gist 大概是 GitHub 上被误解最深的功能:多数人以为它只是「能贴代码的网页」,实际上它是一个有提交历史、能被 git clone、能被嵌入、能调 REST API 的迷你仓库。
这篇把它一次讲清:怎么建、怎么管、怎么用,以及哪些事绝对不能用它做。
一、先纠正三个误解
在讲操作之前,先把最容易出事的认知摆正。
| 常见说法 | 实际情况 |
|---|---|
| 「Gist 就是 GitHub 版的 Pastebin」 | 不止。每个 Gist 就是一个真正的 git 仓库,能 clone、能 push、有逐次提交的历史。贴文本只是它最浅的一层用法。 |
| 「secret gist 是私有的」 | 错,而且是最危险的一条。secret 只是「不列在公开列表里」,任何人拿到链接都能读。Gist 没有私有仓库这个档位。 |
| 「一个 Gist 只能放一个文件」 | 错。一个 Gist 可以放多个文件,但没有目录结构——所有文件平铺在同一层。 |
记住这三条,后面的内容基本就是在它们上面长出来的。
二、Gist 到底是什么
一句话:Gist 是一个没有目录、默认轻量、但完整具备 Git 能力的仓库。
它具备:
- 提交历史:每次编辑都是一个 revision,可以看 diff、可以回到任意一版;
- git 协议:可以
git clone、改完git push; - 协作原语:star、fork、评论;
- REST API:可以脚本化创建、更新、删除;
- 三种链接:给人看的网页、给程序读的 raw、给 git 用的仓库地址。
它不具备:目录结构、Issue、Pull Request、Actions、GitHub Pages、私有可见性、二进制文件。这七条缺失决定了它的适用边界(第八节逐条展开)。
| 对比项 | Gist | Pastebin 类 | 普通仓库 | 私有仓库 |
|---|---|---|---|---|
| 版本历史 | 有(git 提交) | 通常没有 | 有 | 有 |
| 目录结构 | 无,扁平 | 无 | 有 | 有 |
| 访问控制 | 公开 / 「知道链接就能看」 | 类似 | 公开 | 真正的权限控制 |
| 协作 | fork / 评论 | 基本没有 | Issue / PR / Actions | 同左 |
| 适合 | 小片段、日志、一次性文档 | 纯文本临时分享 | 项目 | 内部资产 |
三、三种创建方式
3.1 网页:一次性的、要立刻发出去的东西
打开 https://gist.github.com,填三样东西就完事:
- Filename including extension:这一步不能省。扩展名决定了语法高亮,也决定了渲染方式——
.md会渲染成排版好的文档,.json/.yaml/.sh会按语言高亮,不写扩展名就是纯文本。 - Gist description:会显示在列表和卡片上,建议写清「这是什么、给谁看」。
- 正文:直接粘贴。
右下角两个按钮,就是全部可见性选项:
- Create public gist:会出现在 Discover(
gist.github.com/discover)、可被搜索、可被别人 fork; - Create secret gist:不出现在 Discover、不出现在搜索、不出现在你的公开 gist 列表(未登录访问你的主页看不到)。
3.2 GitHub CLI:日常最省事的方式
安装(各平台一条命令):
# Windows
winget install --id GitHub.cli
# macOS
brew install gh
# RHEL / Fedora
sudo dnf install gh
# Debian / Ubuntu(官方源)
sudo apt install gh
登录一次,之后所有操作免交互:
gh auth login
常用操作:
# 从文件创建(默认 secret,加 -p 才是 public)
gh gist create notes.md --desc "某接口排查笔记"
# 指定文件名、显式公开
gh gist create -f setup.sh -p ./install.sh
# 从标准输入创建:管道里的东西直接变成 gist
echo "hello" | gh gist create -
kubectl logs deploy/api | gh gist create -f api.log
# 列出、查看、取原始内容
gh gist list -L 20
gh gist view <id> --raw
gh gist view <id> --filename a.txt
# 改:加文件、删文件、改描述
gh gist edit <id> --add new.txt
gh gist edit <id> --remove old.txt
gh gist edit <id> --desc "新描述"
# 当仓库用
gh gist clone <id>
# 删(会二次确认)
gh gist delete <id>
| 子命令 | 用途 | 需要注意 |
|---|---|---|
gh gist create | 创建 | 默认 secret,公开必须显式 -p |
gh gist list | 列表 | 默认 10 条,用 -L 放大;可加 --public / --secret 过滤 |
gh gist view | 查看 | --raw 输出原始内容,适合再接管道 |
gh gist edit | 更新 | 每次修改生成新 revision,历史不丢 |
gh gist clone | 克隆 | 之后可以用普通 git 命令提交推送 |
gh gist delete | 删除 | 不可撤销;已被 fork 的副本删不掉 |
3.3 REST API:放进脚本、放进流水线
创建("public": false 即 secret):
curl -sS -X POST https://api.github.com/gists \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
-d '{
"description": "构建产物摘要",
"public": false,
"files": {
"summary.md": { "content": "# 本次构建\n\n全部通过。" }
}
}'
更新、读取、删除:
# 更新某个文件的内容(生成新 revision)
curl -sS -X PATCH https://api.github.com/gists/<id> \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-d '{"files":{"summary.md":{"content":"# 本次构建\n\n2 个用例失败。"}}}'
# 读取(注意:文件大于 1MB 时 content 会被截断,需改用 raw_url)
curl -sS https://api.github.com/gists/<id> \
-H "Authorization: Bearer $GITHUB_TOKEN"
# 删除
curl -sS -X DELETE https://api.github.com/gists/<id> \
-H "Authorization: Bearer $GITHUB_TOKEN"
gist 作用域;fine-grained token 需要显式授予 Gists 权限。API 限流为认证后 5,000 次/小时、匿名 60 次/小时——把 Gist 当数据存储高频读写会先撞上这堵墙。四、public 与 secret:必须记住的一条边界
| public gist | secret gist | |
|---|---|---|
| 出现在 Discover | 是 | 否 |
| 站内搜索可搜到 | 是 | 否 |
| 未登录访问你的主页能看到 | 是 | 否 |
| 知道链接就能打开 | 是 | 是 |
| 知道链接就能 clone / 读 raw | 是 | 是 |
| 需要身份验证才能访问 | 否 | 否 |
最后两行是重点。secret gist 的 ID 是 32 位十六进制——猜不到,但一旦链接出现在任何公开的地方(聊天记录、论坛、issue、截图、浏览器 referer、第三方抓取服务),它就和 public 没有区别了,而且会被搜索引擎收录。
另外两点:
- secret 可以随手改成 public(编辑页有入口),一次误点就等于公开发布,且可能已被抓取;
- public 改回 secret 没有稳定入口,请视为单向操作(以官方界面为准)。
五、文件、修订与 git 用法
5.1 多文件,但没有目录
一个 Gist 里可以有任意多个文件,文件平铺在同一层,文件名必须唯一。文件名写成 docs/readme.md 也不会生成子目录——它只是名字里带了个斜杠,在 URL 里还会被转义。这是 Gist 最硬的限制:只要你想表达「目录」,就该换仓库。
5.2 每一次编辑都是一个可寻址的版本
https://gist.github.com/<user>/<id>:看最新版;https://gist.github.com/<user>/<id>/revisions:看全部历史,逐版 diff;https://gist.github.com/<user>/<id>/<sha>:固定到某一版——这个链接永远指向当时的内容,是「可复现」的关键。
5.3 当成 git 仓库用
# 克隆(三种写法都能用)
git clone https://gist.github.com/<id>.git
git clone https://gist.github.com/<user>/<id>.git
git clone git@gist.github.com:<id>.git
# 之后就是普通的 git 流程
cd <id>
vim something.md
git add -A && git commit -m "补一段说明"
git push
几个容易忽略的点:
- push 是新增提交,不是覆盖——历史永远在,写错了也能翻回来;
- 推送需要凭据:HTTPS 用 PAT(密码方式已不可用于 git 操作),SSH 用已登记的密钥;
- 克隆下来的目录名就是 gist ID,可以自己改名;
- 多个文件改动在一次提交里推上去,只算一个 revision,比在网页上逐个文件点改要干净得多。
六、引用与嵌入
6.1 raw 链接:给程序读
# 永远指向最新版
https://gist.githubusercontent.com/<user>/<id>/raw/<filename>
# 固定到某一版(推荐脚本里用这个)
https://gist.githubusercontent.com/<user>/<id>/raw/<sha>/<filename>
脚本、CI、Dockerfile 里引用外部内容,一律固定到 sha。用「最新版」的写法,意味着别人随时可以改掉你依赖的东西——今天跑通、明天失败的经典来源。
6.2 嵌入网页
<script src="https://gist.github.com/<user>/<id>.js"></script>
<!-- 只嵌入其中某个文件 -->
<script src="https://gist.github.com/<user>/<id>.js?file=notes.md"></script>
<!-- 锁定到某一版 -->
<script src="https://gist.github.com/<user>/<id>/<sha>.js"></script>
嵌入失败时,按这个顺序查:
- 页面的 CSP 是否放行
gist.github.com(我们自己的站点就设了严格的script-src,默认情况下会被浏览器直接拦掉——这属于正常的安全策略,不是 Gist 坏了); - 页面是否禁用了 JS,或用了纯静态渲染;
- 网络能否访问
gist.github.com(内网、代理、地区网络都可能是原因); - secret gist 不要指望能嵌入——官方不提供这个能力,要嵌入就建 public。
七、九个真实场景
下面每一条都能直接抄。
1. 贴一段报错或日志
journalctl -u myservice --since "1 hour ago" | tail -n 300 \
| gh gist create -f myservice.log --desc "服务重启前的 300 行日志"
贴之前先脱敏:把 IP、域名、账号、token、内部主机名替换掉。日志是最容易顺手泄露基础设施信息的东西。
2. 分享一段配置
nginx / systemd / docker-compose 之类,用 -f 指定真实文件名,看的人一眼就知道该放哪。
3. 分享一次性脚本
gh gist create -p -f fix-dns.sh ./fix-dns.sh
curl ... | bash 这种写法,就必须给出固定 sha 的链接,并在说明里写清脚本做了什么。让别人执行一段随时会变的内容,等于让人把钥匙交给一个会换锁的人。4. 一份短文档
文件名用 .md,会直接渲染成排版好的页面(标题、列表、表格、代码块都支持)。比新建仓库轻,比发聊天记录可读。
5. 临时的「改动前 / 改动后」对比
把旧版本先建成一个 gist,改完再建一个,然后用 gist 的 revisions 页面看 diff;或者直接把两份文件放进同一个 gist 的两个文件里,谁改了什么一目了然。
6. 给同事或 AI 的上下文包
一个 README.md 写清目标与约束,加两三个样例文件(输入样本、期望输出、错误日志),链接发出去即可。这比在聊天窗口里贴十几段代码有效得多——对方拿到的是有结构、可引用的材料。
7. 轻量的配置分发
curl -sSL https://gist.githubusercontent.com/<user>/<id>/raw/<sha>/agent.yaml \
-o /etc/myapp/agent.yaml
8. 嵌进博客或文档站
见 6.2。适合「示例代码需要长期维护、但不想复制粘贴到文章里」的场景。
9. 当一个小 JSON 数据源
curl -sS https://api.github.com/gists/<id> \
| jq -r '.files["data.json"].content'
能做,但要克制:没有并发控制、没有事务、公开可读、有 CDN 缓存、有 API 限流。它适合「人工偶尔改一次的小配置」,不适合当数据库。
八、十一个一定会踩的坑
| # | 坑 | 表现 / 应对 |
|---|---|---|
| 1 | 不支持二进制 | 上传图片、zip、pdf、字体都会失败。Gist 不是图床,也不是文件传输工具。 |
| 2 | 没有目录 | 所有文件平铺;用斜杠命名不会建子目录。结构化内容请转仓库。 |
| 3 | 单文件有体积上限 | API 返回的 content 超过 1MB 会被截断(truncated: true),必须改用 raw_url。大文件浏览体验也很差。 |
| 4 | raw 有 CDN 缓存 | 更新后立刻访问 raw 可能还是旧内容。调试时加个随机查询参数(?t=...)绕过缓存。 |
| 5 | 匿名 Gist 已取消 | 2018 年起必须登录才能创建。Gist 一定有归属者。 |
| 6 | secret 会被索引 | 链接一旦公开引用,搜索引擎与第三方镜像就可能收录,且删不干净。 |
| 7 | 不能转让所有权 | Gist 属于创建者。人离职、账号停用,链接就跟着没了。团队资产请放组织仓库。 |
| 8 | 没有 Issue / PR / Actions / Pages | 它不是项目载体。想要这些能力,第一天就该建仓库。 |
| 9 | API 有限流 | 认证 5,000 次/小时、匿名 60 次/小时;批量脚本要把 Gist 调用当「昂贵操作」看待。 |
| 10 | 删除不等于消失 | 别人 fork 过的副本、缓存、镜像都还在。所以泄露处置的第一步永远是吊销凭据,不是删文件。 |
| 11 | 网页端编辑大文件很痛苦 | 几百行以上建议 git clone 到本地改完再 push。 |
九、安全清单
发布前自查(贴在习惯里,比事后补救便宜一万倍):
grep -nEi '(api[_-]?key|secret|token|passw(or)?d|BEGIN [A-Z ]*PRIVATE KEY|ghp_|github_pat_|sk-)' 待发布文件
万一已经发出去了,按这个顺序处理:
- 先吊销——token 撤销、密钥轮换、口令改掉;
- 再删除或修改 Gist 内容;
- 查使用记录(API 日志、登录记录、云厂商审计),确认有没有被用过;
- 通知相关方,评估影响面。
另外两条原则:
- secret gist 也按公开对待——这是唯一安全的默认假设;
- 团队内部共享走私有仓库,不要用 secret gist 当「内部资料区」。
十、速查表
| 我要… | 怎么做 |
|---|---|
| 最快贴一段文本 | 打开 gist.github.com,填文件名 → 粘贴 → Create |
| 发一条只看得到、搜不到的链接 | 创建时选 secret(并记住它不是私有) |
| 把日志直接变成链接 | cmd | gh gist create -f out.log |
| 公开分享 | gh gist create -p file |
| 改内容并留下历史 | gh gist edit <id> 或 clone 后 git push |
| 固定引用某一版 | 用 /<sha> 链接,不要用最新版 |
| 嵌进网页 | <script src="https://gist.github.com/<user>/<id>.js"></script>(注意 CSP;仅 public) |
| 脚本化创建/更新 | POST/PATCH https://api.github.com/gists |
| 删掉 | gh gist delete <id>(不可撤销,fork 副本仍在) |
| 放图片 / zip / 需要目录 / 需要权限 | 不要用 Gist,用仓库或对象存储 |
十一、结语:一条判断标准
Gist 的定位很清楚:临时的、单层的、要被立刻看到的、需要一点点版本能力的东西。它把「贴一段代码」这件事的成本压到了最低,同时保留了 Git 的可追溯性——这就是它十几年没过时的原因。
反过来,凡是需要目录、二进制、权限、多人协作、长期维护的,它都不合适,勉强用只会把问题推迟到更贵的时候爆发。
所以我们自己的判断标准只有一句话:
如果你会为它写第二个 README,它就不该是 Gist。
工具的价值不在于能做多少事,而在于知道它在哪一刻该停。Gist 是这样,我们做系统时对待每一个「临时方案」也是这样。