跳转到内容

第 9 章 · GitHub

一句话:托管 Git 远程仓库的网站,围绕仓库提供拉取请求、Issue、版本发布和权限控制;gh 是它的官方命令行工具,在终端里完成这些网页操作。 用它还是用别的:开源项目和大多数团队用 GitHub;公司要求代码放在国内或自己的服务器上时用 Gitee 或自建 GitLab,Git 命令一模一样,差别见 9.5 末尾的对照表。 核对版本:gh 2.102,2026-10

先在 github.com 用邮箱注册账号,邮箱最好和第 7 章 git config --global user.email 设的那个一致。

Windows:

终端窗口
winget install GitHub.cli

macOS:

终端窗口
brew install gh

Ubuntu 和 Debian 自带软件源里的 gh 落后官方版本很多,先按 gh 手册里 Linux 安装说明的 Debian 一节添加 GitHub 官方 apt 源(链接见 9.7),再安装:

终端窗口
sudo apt install gh

查版本:

终端窗口
gh --version

装好后登录一次。命令会依次问:登录 GitHub.com 还是企业版服务器,Git 走 HTTPS 还是 SSH,然后在浏览器里输入终端显示的一次性验证码。选 HTTPS 时它会问要不要让 Git 也用这份登录,选是,以后 git push 不再要密码:

终端窗口
gh auth login

令牌存进系统的凭据存储(Windows 凭据管理器、macOS 钥匙串);没有凭据存储的 Linux 服务器上,以明文写在 gh 的配置目录里,见 9.5。

  • 远程仓库(Repository):GitHub 上的仓库就是第 7 章说的远程,本地 git clone 下来的那份默认把它叫 origin。提交、分支、标签都在本地做,git push 之后网页上才看得到。
  • Fork:把别人的仓库在 GitHub 上复制一份到自己名下。你对原仓库没有写权限,就在自己的 fork 上推分支,原仓库习惯叫上游(upstream)。
  • 拉取请求(Pull Request,PR):请求把一个分支合并进另一个分支,可以来自同一仓库,也可以来自 fork。评审意见、CI 检查结果、讨论都挂在 PR 上;PR 没关之前,往这个分支再推提交,PR 会自动更新。
  • Issue:记录 bug、需求和任务的条目,每个有编号,如 #12。提交说明或 PR 说明里写 Fixes #12,合并后这个 Issue 自动关闭。
  • 保护分支(Branch protection)与规则集(Rulesets):给 main 这类分支加规则,比如必须走 PR、必须有人批准、CI 必须通过、不许强推。规则集是较新的写法,一套规则可以同时管多个分支,效果与保护分支相同。
  • 两种认证方式:HTTPS 地址用个人访问令牌(Personal Access Token,PAT)代替密码,gh auth login 会替你管理这个令牌;SSH 地址用密钥,生成和添加步骤见第 6 章 6.4。同一台机器选一种就够。

<所有者>/<仓库> 写成 cli/cli 这样。PR 相关的命令不写编号时,指当前分支对应的那个 PR。表的最后一组在网页上操作,路径按 GitHub 中文文档的写法。

任务 命令或操作 说明
仓库
新建仓库并克隆到本地 gh repo create <名> --private --clone 公开仓库把 --private 换成 --public
把本地已有项目建成仓库并推送 gh repo create <名> --private --source=. --push 代替第 7 章在网页上建空仓库、加远程、推送那几步
克隆 gh repo clone <所有者>/<仓库> 克隆的是 fork 时自动加上 upstream 远程
Fork 并克隆 gh repo fork <所有者>/<仓库> --clone 你的 fork 是 origin,原仓库是 upstream
让 GitHub 上的 fork 跟上上游 gh repo sync <你>/<仓库> 只做快进;分叉了要加 --force,fork 上多出的提交会丢
在浏览器打开当前仓库 gh browse 后面加编号打开对应 Issue 或 PR
拉取请求
用提交说明建 PR gh pr create --fill 标题和正文取自提交说明,分支还没推送时会先问推到哪
建草稿 PR gh pr create --draft --fill 草稿不能合并,适合边做边看 CI
草稿改为可评审 gh pr ready
请人评审 gh pr edit --add-reviewer <用户名> 多人用逗号隔开
列出打开的 PR gh pr list 加 --author "@me" 只看自己的
和我有关的 PR gh pr status 当前分支、我建的、等我评审的,带检查结果
看 PR 的 CI 检查 gh pr checks --watch 一直刷新到全部检查结束
把别人的 PR 拉到本地试 gh pr checkout <编号> 来自 fork 的 PR 也行
在浏览器看 PR gh pr view --web 评审逐行评论在网页上更方便
批准 gh pr review <编号> --approve
要求修改 gh pr review <编号> -r -b "<意见>"
压成一个提交合并并删分支 gh pr merge --squash --delete-branch 本地和远程的分支一起删
Issue
新建 gh issue create --title "<标题>" --body "<内容>" 不带参数就逐项提问
列出打开的 gh issue list 加 --label bug 只看带这个标签的
关闭 gh issue close <编号> --comment "<说明>"
合并时自动关闭 提交或 PR 说明里写 Fixes #12 合并进默认分支才生效;Closes、Resolves 也行
发布
推送一个标签 git push origin v1.0 打标签的命令见第 7 章 7.3
建发布,自动生成说明 gh release create v1.0 --generate-notes 说明列出上次发布以来合并的 PR
建发布时带上附件 gh release create v1.0 <文件> --generate-notes 文件出现在发布页的下载列表里
给已有发布补传附件 gh release upload v1.0 <文件> 同名文件要加 --clobber 才覆盖
Actions(CI)
最近的运行记录 gh run list 加 --branch <分支> 只看一个分支;写 CI 见第 10 章
盯着一次运行到结束 gh run watch 不写运行编号会让你从列表里选
认证
看当前登录的账号 gh auth status 每个账号一段,标出哪个正在使用
让 Git 推拉用 gh 的登录 gh auth setup-git 登录时没选让 Git 用这份登录的,补这一步
切换账号 gh auth switch 公司号和私人号都登录过时用
网页上的操作
设保护分支 Settings → 分支 → 添加经典分支保护规则 或 Settings → 规则集 → 新建分支规则集;勾哪些见 9.5
改默认分支 Settings → 默认分支名称右侧的切换按钮 → 更新 PR 默认合并的目标、Fixes #12 生效的分支都跟着变
PR 和 Issue 模板 .github/PULL_REQUEST_TEMPLATE.md、.github/ISSUE_TEMPLATE/ 合并进默认分支后,新建 PR 和 Issue 时自动填入
指定代码负责人 .github/CODEOWNERS 改到对应文件的 PR 自动请这些人评审

你对这个项目没有写权限,所以先 fork 一份,在自己的 fork 上推分支,再从 fork 向原仓库发 PR:

  1. Fork 并克隆,然后进入克隆出的目录:gh repo fork <所有者>/<仓库> --clone。
  2. 确认有两个远程,origin 是你的 fork,upstream 是原仓库:git remote -v。
  3. 开新分支,不要直接在 main 上改:git switch -c fix/<名>。
  4. 改好后提交:git commit -am "<说明>"。
  5. 推到自己的 fork:git push -u origin fix/<名>。
  6. 发 PR,gh 已把原仓库设为默认仓库,PR 发到它的默认分支:gh pr create --fill。
  7. 维护者提了修改意见,改完提交后再推,PR 自动更新:git push。
  8. PR 合并后,切回主分支:git switch main。
  9. 把上游合并进来的提交拉回本地:git pull --ff-only upstream main。

GitHub 上的 fork 也要跟上时,再 git push origin main,或者 gh repo sync <你>/<仓库>。

在网页上点 Fork 再 git clone 的,要手动 git remote add upstream <原仓库地址>;第一次 gh pr create 时它会问以哪个仓库为基准,选原仓库。

图里两个仓库都在 GitHub 上:① 到 ③ 是你的操作,代码只进你的 fork;④ 的 PR 从 fork 指向上游,⑤ 由维护者合并;⑥ 是第 9 步,上游的新提交回到本地,下一个分支从这里开始。

上游仓库 Fork 出你的 fork,克隆到本地;本地推分支回 fork,从 fork 向上游发 PR 并由维护者合并,再从上游把新提交拉回本地

你有仓库的写权限,不用 fork。接着第 7 章“一天的分支开发”往下走:

  1. 按第 7 章那个工作流的第 1 到 6 步开分支、提交,最后推送:git push -u origin feature/<名>。
  2. 建草稿 PR,CI 开始跑:gh pr create --draft --fill。
  3. 等 CI 结果,有失败就改了再推:gh pr checks --watch。
  4. CI 全部通过,改为可评审:gh pr ready。
  5. 请同事评审:gh pr edit --add-reviewer <用户名>。
  6. 同事在自己机器上试运行这个 PR:gh pr checkout <编号>。
  7. 同事批准:gh pr review <编号> --approve。
  8. 压成一个提交合并,删掉本地和远程的功能分支:gh pr merge --squash --delete-branch。
  9. 回到主分支:git switch main。
  10. 取回合并结果:git pull。

squash 合并把功能分支上的零碎提交压成 main 上的一个提交,提交说明默认用 PR 标题,所以 PR 标题要写清楚做了什么。

  1. 回到主分支:git switch main。
  2. 取回最新提交:git pull。
  3. 打标签:git tag -a v1.0 -m "<说明>"。
  4. 推送标签:git push origin v1.0。
  5. 建发布,说明自动列出上次发布以来合并的 PR;--verify-tag 在远程没有这个标签时直接报错,防止 gh 自己在最新提交上新建一个:gh release create v1.0 --generate-notes --verify-tag。
  6. 上传安装包等附件:gh release upload v1.0 <文件>。
  7. 在浏览器里检查发布页:gh browse --releases。

gh 自己的配置文件:

配置项 Windows macOS Linux
gh 设置 %APPDATA%\GitHub CLI\config.yml ~/.config/gh/config.yml ~/.config/gh/config.yml
登录过的账号 %APPDATA%\GitHub CLI\hosts.yml ~/.config/gh/hosts.yml ~/.config/gh/hosts.yml

令牌本身在系统凭据存储里,没有凭据存储时才以明文写进 hosts.yml,这个文件别提交、别拷给别人。值得改的只有两项,用命令改,不用手编辑文件:gh config set git_protocol ssh 让 gh repo clone、gh repo fork --clone 用 SSH 地址(已按第 6 章配好密钥时);gh config set editor "code --wait" 让写 PR 和 Issue 正文时打开 VS Code。

仓库里 .github/ 目录下的文件,合并进默认分支才生效:

配置项 作用
PULL_REQUEST_TEMPLATE.md 新建 PR 时正文自动填入的模板,文件名不分大小写
ISSUE_TEMPLATE/ Issue 模板,一个文件一种;.md 是普通模板,.yml 是表单
ISSUE_TEMPLATE/config.yml blank_issues_enabled: false 禁止不用模板建 Issue
CODEOWNERS 一行一条,如 *.js @<用户名>,改到这些文件的 PR 自动请此人评审
workflows/ GitHub Actions 的流水线文件,见第 10 章

保护分支值得开的四项。经典分支保护规则和规则集里的叫法不同,按下表找:

要做到 经典分支保护规则里勾 规则集里勾 效果
必须走 PR 合并前需要 Pull Request 在合并前需要拉取请求 谁都不能直接推到 main
至少一人批准 要求审批,人数填 1 同一项下的需要批准,人数填 1 自己批准自己不算
CI 通过才能合并 合并前需要通过状态检查 要求合并前必须通过状态检查 要在列表里选具体的检查名,工作流至少跑过一次后,检查名才会出现在列表里
禁止强推 不勾允许强制推送 阻止强制推送 main 的历史不会被 --force 改写

经典规则默认管理员可以绕过,要连自己一起管,再勾“不允许绕过上述设置”。

最小的 .github/PULL_REQUEST_TEMPLATE.md,注释在 PR 页面上不显示:

## 改了什么
<!-- 一两句话,评审的人先看这里 -->
## 为什么
<!-- 关联的 Issue 写 Fixes #编号,合并后自动关闭 -->
## 怎么验证
<!-- 跑了哪些测试,或手动点了哪几步 -->
- [ ] 本地测试通过 <!-- 勾选框,评审前自己打勾 -->

换到 GitLab 或 Gitee,Git 命令和本章的流程都一样,只是名字和工具不同:

GitHub GitLab Gitee
拉取请求(Pull Request,PR) 合并请求(Merge Request,MR) Pull Request
GitHub Actions,配置在 .github/workflows/ GitLab CI/CD,配置在 .gitlab-ci.yml Gitee Go 流水线
Issue Issue Issue
Release Release 发行版(Release)
命令行工具 gh 命令行工具 glab 无官方命令行工具
  • git push 要密码,输入 GitHub 登录密码后报 Support for password authentication was removed。 原因:GitHub 不接受账号密码做 Git 认证。处理:gh auth login 后运行 gh auth setup-git;或在密码处粘贴个人访问令牌;或改用 SSH 地址,见第 6 章 6.4。
  • remote: Permission to <所有者>/<仓库>.git denied to <另一个用户名>. 原因:Git 用了另一个 GitHub 账号的凭据,常见于公司号和私人号混用。处理:gh auth status 看当前账号,gh auth switch 切换;Windows 上再到凭据管理器删掉 git:https://github.com 那一条。
  • PR 里出现几十个不是你写的提交。 原因:基准分支选错,或者功能分支是从很久没更新的本地 main 开出来的。处理:基准分支错了用 gh pr edit --base main;否则 git fetch upstream,git rebase upstream/main,再 git push --force-with-lease。
  • fork 页面显示落后上游很多个提交(commits behind)。 原因:fork 之后不会自动跟随上游。处理:gh repo sync <你>/<仓库>;本地分支用 git fetch upstream 后 git rebase upstream/main。
  • 说明里写了 Fixes #12,PR 合并了,Issue 还开着。 原因:只有合并进默认分支才会自动关闭,这个 PR 合进了 develop 这类分支。处理:手动 gh issue close 12;以后在发往默认分支的 PR 里写。
  • push 被拒,remote: error: GH001: Large files detected. 原因:GitHub 拒收超过 100 MiB 的单个文件,50 MiB 以上就会警告。处理:大文件改用 Git LFS,git lfs track "*.psd";已经在历史里的,按第 8 章 8.4 撤销表从历史中删掉再推。
  • 开了保护分支后,自己 git push origin main 也被拒,GH006: Protected branch update failed。 原因:预期行为,规则对有写权限的人一样生效;规则集报的是 GH013: Repository rule violations found。处理:开分支走 PR,见 9.4。
  • 提交在 GitHub 上显示灰色默认头像,没链接到你的账号。 原因:提交里的 user.email 不是 GitHub 账号里登记的邮箱。处理:按第 7 章 7.4 改 user.email;或在 GitHub 账号设置里添加这个邮箱,旧提交也会关联上。
  • GitHub 文档(中文):仓库、拉取请求、Actions、账号和认证的官方说明,有中文。
  • GitHub CLI 手册:gh 每条子命令的用法和参数,安装一节里有 Linux 的官方 apt 源。
  • 拉取请求:建 PR、评审、合并方式和解决冲突,有中文。
  • 关于受保护分支:保护分支每一项规则的含义,以及和规则集的关系。
  • GitLab 文档:合并请求、GitLab CI/CD 和 glab 命令行工具。
  • Gitee 帮助中心:Gitee 的 Pull Request、Issue、发行版和 Gitee Go 流水线。