GitHub CLI 之 gh 命令

摘要

gh 是什么

  • 开 Pull Request、发 Release、看 Actions 发生在 GitHub 上。git 管本地提交和分支,到不了这些对象。gh 把它们收成仓库目录里的命令,也可以放进脚本,不用每次打开网页。

  • gh 调用 GitHub 的 HTTP API,用来登录、建仓库、开 Pull Request、看 Actions。工作区里的 git commit、git push、git pull 仍由 git 完成。

  • 在一个已经关联 GitHub 远程的目录里执行时,gh 从 origin 读出 OWNER/REPO。不在仓库目录里时,用 -R OWNER/REPO,或设置环境变量 GH_REPO。

  • 子命令的参数以本机 gh <命令> --help 为准。gh help 列出全部命令。

安装

macOS

  • Homebrew 安装 gh 这个 formula。装完用 gh version 确认,输出里应有 2.102.0 这一行。

1
2
brew install gh
gh version

Linux

  • Fedora、CentOS、RHEL 使用 GitHub 提供的 rpm 仓库。dnf install gh 装的是仓库里当前的包,装完仍用 gh version 核对是不是 2.102.0。

1
2
3
4
sudo dnf install -y 'dnf-command(config-manager)'
sudo dnf config-manager --add-repo https://cli.github.com/packages/rpm/gh-cli.repo
sudo dnf install -y gh
gh version

登录

  • 默认走浏览器。令牌写入系统凭据库。找不到凭据库时,退回到 ~/.config/gh/hosts.yml 明文文件。gh auth status 会打印实际存放位置。

1
2
3
4
5
6
7
8
# 交互式登录。git 协议选 https 或 ssh
gh auth login

# 直接打开浏览器,并把一次性设备码复制到剪贴板
gh auth login --web --clipboard

# 指定 git 操作用的协议。ssh 会尝试上传本机已有的公钥
gh auth login --git-protocol ssh --skip-ssh-key
  • 经典 personal access token 从标准输入读入。最小范围是 repo、read:org、gist。细粒度 token 范围窄,交互命令容易在别的仓库上报权限错误,脚本里改用环境变量 GH_TOKEN。

1
gh auth login --with-token < mytoken.txt
  • GH_TOKEN 优先于 gh auth login 存下来的凭据。gh auth status 显示已登录,只说明凭据库里有账号。当前 shell 如果设置了环境变量 GH_TOKEN,这次请求用的是这个令牌。

1
2
3
# 只对当前 shell 生效。关掉终端后消失
export GH_TOKEN='ghp_换成你的令牌'
gh auth status
  • GitHub Enterprise Server 换主机名,令牌用 GH_ENTERPRISE_TOKEN。

1
gh auth login --hostname enterprise.example.com
  • 让 git 走 HTTPS 时使用 gh 保存的凭据。这一步改的是 Git 配置,不是只改当前进程。

1
2
gh auth setup-git
gh auth status
  • 多账号用 gh auth switch 切换当前主机上的活动账号。gh auth logout 删掉该主机上保存的登录状态。

gh auth status 能通过,不代表紧接着的 gh api 或 gh pr create 一定用的是同一个账号。先看当前环境里有没有 GH_TOKEN。

仓库

  • 仓库参数写成 OWNER/REPO,或完整的 https://github.com/OWNER/REPO。省略 OWNER/ 时,用当前登录用户的名字。

命令 作用
gh repo create 在 GitHub 上新建仓库
gh repo clone 克隆。参数写在 -- 后面才传给 git clone
gh repo list 列出某个用户或组织的仓库
gh repo view 看仓库说明。--web 用浏览器打开
gh repo fork 派生一份到自己的账号
gh repo sync 把上游提交同步到当前检出或指定分支
gh repo set-default 当前目录对应多个远程时,指定默认的 OWNER/REPO
1
2
3
4
5
6
7
8
# 在当前目录对应的本地仓库之外,新建一个公开仓库并克隆下来
gh repo create my-project --public --clone

# 用当前目录已有的 git 仓库作为内容,远程名用 upstream
gh repo create my-project --private --source=. --remote=upstream

# -- 后面的 --depth=1 是 git clone 的参数
gh repo clone cli/cli -- --depth=1
  • 克隆的是 fork 时,gh 会再加一个名为 upstream 的远程,指向父仓库,并把父仓库设为这个目录的默认仓库。只要 origin 时加上 --no-upstream。

1
gh repo clone my-fork --no-upstream

Pull Request

  • 编号、Pull Request 的 URL,或头分支名都可以当作参数。gh pr checkout 的短别名是 gh co。

命令 作用
gh pr create 创建。成功后把 URL 打到标准输出
gh pr list 列出当前仓库的 Pull Request
gh pr status 看和自己有关的 Pull Request
gh pr view 查看。--web 打开浏览器
gh pr diff 看改动
gh pr checkout 把该 Pull Request 的头分支检出到本地
gh pr checks 看这一次的 CI 状态
gh pr comment 加评论
gh pr review 提交评审
gh pr merge 合并
gh pr ready 把草稿标成可以评审
gh pr close 关闭,不合并
  • 当前分支还没推到远程时,gh pr create 会询问推到哪里,并可以顺手 fork。标题和正文不写就会继续问。--fill 用已有 commit 填这两项。同时写了 --title 或 --body 时,以这两个参数为准。

  • --dry-run 打印将要创建的内容,不真正创建 Pull Request。它仍可能把分支推到远程。

1
2
3
4
5
6
7
8
# 推送当前分支并创建。--base 是要合入的分支,省略时用仓库默认分支
gh pr create --base main --title "修复登录超时" --body "重试一次后再报错。"

# 草稿。正文里的 Fixes #123 会在合并后关闭对应 issue
gh pr create --draft --fill --body "Fixes #123"

# 指定评审人。团队写成 org/team-name
gh pr create --reviewer monalisa --reviewer myorg/team-name
  • 合并方式要和仓库允许的方式一致。仓库只开了 squash 时,--merge 会被拒绝。

1
2
3
4
5
# 合并后删除本地和远程的头分支
gh pr merge 321 --squash --delete-branch

# 检查还没过时先排队,条件满足后由 GitHub 合并
gh pr merge 321 --squash --auto
  • 有仓库管理员权限、又要绕过分支保护时用 --admin。

Issue

命令 作用
gh issue create 新建。不给 --title 时会询问
gh issue list 列出
gh issue view 查看。参数是编号或 issue URL
gh issue comment 加评论
gh issue close 关闭
gh issue reopen 重新打开
1
2
3
4
5
gh issue create --title "登录页在空密码时没有提示" --label bug --assignee @me

gh issue list --label bug --state open
gh issue view 123
gh issue close 123 --comment "已由 #124 修复"

Release

  • gh release create 的第一个参数是 tag。--target 不写时用仓库默认分支。tag 在远程还不存在时,GitHub 会按这个 target 创建。只要远程已有的 tag 时,加上 --verify-tag,不存在就中止。

1
2
3
4
5
# 标题和说明由 GitHub 按提交记录生成,并附上本地的 dist 目录里的文件
gh release create v1.0.0 --generate-notes ./dist/*

# 先建草稿,确认后再发布
gh release create v1.0.0 --draft --title "1.0.0" --notes "第一次发布"
命令 作用
gh release list 列出
gh release view 查看某一个 tag
gh release download 下载该 release 的附件
gh release upload 往已有 release 追加附件
gh release delete 删除 release。加上 --cleanup-tag 同时删掉这个 tag。--yes 跳过确认
1
gh release download v1.0.0 --dir ./downloads

Actions

  • gh run list 默认最多取 20 条。gh auth status 能通过,只说明账号有效。工作流没开、或 token 没有 actions 读权限时,列表仍会是空的或报 403。

1
2
3
4
5
6
7
8
# 只要失败的、由 main 触发的运行
gh run list --branch main --status failure --limit 10

# 看到结束为止。中途失败时这条命令以非零状态退出
gh run watch

# 下载该次运行产出的 artifact
gh run download
  • 工作流文件本身用 gh workflow list 和 gh workflow view 看。重新跑用 gh run rerun,取消用 gh run cancel。

直接调 API

  • gh api 在已登录的前提下请求 GitHub REST。路径里的 {owner}、{repo}、{branch} 用当前仓库补上。只加了 -f 或 -F、又没写 --method 时,方法会改成 POST。

1
2
3
4
5
# 只读。jq 表达式从 JSON 里取出标题
gh api repos/{owner}/{repo}/issues --jq '.[].title'

# 翻完所有页。每页仍是一段 JSON
gh api repos/{owner}/{repo}/issues --paginate --jq '.[].number'
  • GraphQL 的端点名是 graphql。除 query 以外的 -f 会变成 GraphQL 变量。

配置

  • 配置目录默认是 ~/.config/gh。GH_CONFIG_DIR 可以改到别处,改的是以后每次 gh 读取的位置。

1
2
3
# 克隆和推送默认走 ssh。取值还有 https
gh config set git_protocol ssh
gh config get git_protocol
  • 脚本里不要让命令停下来提问时,把 prompt 设成 disabled,或设置环境变量 GH_PROMPT_DISABLED。前者写进配置文件,后者只影响当前进程。

变量 作用
GH_TOKEN 访问 github.com 或 ghe.com 子域时使用的令牌,优先于已保存的登录
GH_ENTERPRISE_TOKEN GitHub Enterprise Server 的令牌
GH_HOST 命令里推断不出主机名时用这个主机
GH_REPO 形如 OWNER/REPO。当前目录不是目标仓库时用它
GH_DEBUG 设成任意非空值时,把调试信息打到标准错误。设成 api 时再打印 HTTP 细节

卸载

  • 先退出登录,再卸包。只卸包时,~/.config/gh 里的主机配置还在,下次装回会继续用。

1
2
3
4
5
6
7
gh auth logout --hostname github.com

# macOS,Homebrew 安装的
brew uninstall gh

# Linux,dnf 安装的
sudo dnf remove -y gh
  • 确认命令已经不在 PATH 里:

1
command -v gh
  • 没有输出就说明可执行文件已经删掉。还要清掉登录配置时,再删 $HOME/.config/gh。