GitHub Pull Request:从创建分支到代码合并

适合读者:刚接触 Git/GitHub 协作、需要向团队项目提交代码或参与开源贡献的开发者。本文以 Go 项目为例,但 Git 工作流适用于绝大多数语言和项目。

修改完项目代码后,通常要创建 Pull Request(简称 PR)交给维护者审核。下面分两种场景演示:你有项目的写入权限;你需要通过 Fork 给别人的开源项目贡献代码。

1
2
3
4
5
6
7
8
9
10
11
flowchart LR
A[Local branch] --> B[Code and tests]
B --> C[Commit]
C --> D[Push]
D --> E[Pull Request]
E --> F[Review and CI]
F --> G[Merge]
classDef navy fill:#17365d,color:#fff,stroke:#17365d;
classDef green fill:#147d64,color:#fff,stroke:#147d64;
class A,B,C,D navy;
class E,F,G green;

配图说明:界面截图来自 GitHub 官方文档;流程图是内嵌的 Mermaid 代码块,不依赖 Mermaid Ink 图片服务,也不需要本地图片文件夹。Typora 中如未显示流程图,请在偏好设置中开启图表(Mermaid)功能;不同博客平台对 Mermaid 的支持不同,发布前请先预览。

一、PR 是什么

PR 是向代码仓库发起的合并请求:告诉维护者“我在某个分支完成了这些修改,希望你们审核后将其合并到目标分支”。创建 PR 本身不会合并代码,它提供代码比较、讨论、自动化检查和审查的场所。

PR 涉及两个角色:

  • base(目标):你希望代码最终进入的仓库和分支,如原项目的 main。
  • compare(来源):保存你修改的仓库和分支,如你的 fix/api-timeout。

典型流程如下:

1
2
3
4
5
6
7
8
9
10
11
flowchart TB
A[Sync target branch] --> B[Create feature branch]
B --> C[Edit code and run tests]
C --> D[Review git diff]
D --> E[Git commit]
E --> F[Git push]
F --> G[Open pull request]
G --> H[CI and code review]
H --> I{Changes requested?}
I -->|Yes| C
I -->|No| J[Merge PR]

图 1:从同步 main 到 PR 审核合并的完整流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
同步目标分支
↓
创建独立开发分支
↓
修改代码并运行测试
↓
检查 Diff → Commit
↓
Push 到 GitHub
↓
创建 PR → CI 检查 → 代码审查
↓
根据反馈继续提交 → 合并

在开始之前,建议先阅读仓库的 README.md、CONTRIBUTING.md 和已有 PR,确认项目使用 main 还是 develop 作为目标分支,以及是否规定了分支命名、提交格式或测试命令。

二、场景 A:你对项目有写入权限

如果你是团队成员,可以直接在项目仓库中创建分支并推送。下面假设目标分支为 main,修改内容是修复 API 超时问题。

1
2
3
4
5
6
7
8
flowchart TD
A{Write access to upstream?}
A -->|Yes| B[Create branch in upstream repo]
A -->|No| C[Fork upstream to your account]
C --> D[Create branch in your fork]
B --> E[Push changes]
D --> E
E --> F[Open PR against upstream base]

图 2:先根据仓库权限判断使用普通分支还是 Fork;两者的 PR 来源仓库不同。

第 1 步:确认仓库与工作区状态

1
2
3
4
5
cd /path/to/your-project

git status
git branch -a
git remote -v

通过 git status 确认没有意外的未提交修改。如果有尚未完成的工作,可以先提交到当前分支,或者在确认内容后使用 git stash 暂存;不要直接运行可能覆盖改动的命令。

第 2 步:同步目标分支

1
2
git switch main
git pull --ff-only origin main

--ff-only 只接受快进更新,避免在同步时意外生成额外的合并提交。如果项目的目标分支不是 main,请替换为实际分支。

第 3 步:创建开发分支

1
git switch -c fix/api-timeout

每个独立功能或问题使用单独分支,便于审查和回滚。常见命名习惯如下:

前缀 适用场景 示例
feat/ 新功能 feat/user-login
fix/ Bug 修复 fix/api-timeout
refactor/ 不改变外部行为的重构 refactor/database-layer
docs/ 文档变更 docs/update-readme
test/ 测试补充 test/api-errors

第 4 步:修改代码并完成检查

写完代码后,至少检查格式、构建和测试。对于常规 Go 项目,可以使用:

1
2
3
4
5
6
7
8
9
10
11
# 仅格式化你实际修改的 Go 文件,避免带入无关格式调整
gofmt -w path/to/changed.go

# 构建当前模块中的全部包
go build ./...

# 运行全部包的测试
go test ./...

# 基础静态分析
go vet ./...

注意:这些命令只是通用示例。多模块仓库、需要数据库的集成测试或带代码生成步骤的项目,可能要求使用项目自己的 Makefile、脚本或 CI 流程。实际执行哪些检查,以仓库要求为准。

第 5 步:检查、暂存、提交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看有哪些文件被修改
git status

# 逐项确认具体改动
git diff

# 建议优先精确暂存,避免误提交 .env、日志和生成文件
git add path/to/changed.go path/to/changed_test.go

# 再次核对真正要提交的内容
git diff --cached

# 提交
git commit -m "fix: handle API timeout errors"

如果确定所有变更都属于本次 PR,也可以使用 git add .,但在此之前应当检查改动清单。不要提交密钥、令牌、私有配置、测试数据库或无关的大文件。

提交信息可以采用 Conventional Commits 风格,例如:

1
2
3
4
feat: add user login
fix: handle API timeout errors
refactor: simplify database queries
docs: update setup instructions

第 6 步:推送开发分支

1
git push -u origin fix/api-timeout

首次推送时,-u 设置上游分支;后续在同一分支上通常直接执行 git push 即可。

第 7 步:在 GitHub 上创建 PR

打开 GitHub 仓库。推送新分支后,页面通常会显示 Compare & pull request 按钮;如果没有,也可以进入仓库的 Pull requests → New pull request 页面。

GitHub 仓库推送新分支后出现 Compare and pull request 按钮

图 3:推送新分支后,点击绿色的 Compare & pull request。图片来源:GitHub 官方文档。

也可以先打开仓库的分支选择菜单,确认已切换到刚刚推送的开发分支:

GitHub 分支选择菜单真实截图

图 4:GitHub 分支菜单。图片来源:GitHub 官方文档。

确认比较方向:

1
2
3
4
base repository:    原项目仓库
base branch: main
head repository: 原项目仓库
compare branch: fix/api-timeout

填写标题、修改说明和测试结果,检查 Files changed,确保没有混入无关修改。准备好请求审查后,点击 Create pull request;如果还需要继续开发,可先创建 Draft pull request。

一个清晰的 PR 描述示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
## 背景

特定网络情况下,API 超时未被正确转换为可识别的错误。

## 本次修改

- 处理 API 请求超时场景。
- 补充相关单元测试。
- 调整错误日志信息。

## 验证情况

- [x] `go build ./...`
- [x] `go test ./...`
- [x] `go vet ./...`

## 关联问题

Closes #123

以上勾选情况与 Issue 编号仅作模板演示。真实 PR 必须填写你实际运行通过的检查;没有对应 Issue 就删除关联问题部分。

三、场景 B:向别人的开源项目提交 PR(Fork 工作流)

如果你没有原项目的写入权限,一般先在 GitHub 网页点击 Fork,将原项目复制到自己的 GitHub 账号下,然后把分支推送到自己的 Fork,再向原项目发起跨仓库 PR。

GitHub 仓库页面的 Fork 按钮官方截图

图 5:GitHub 仓库页面右上角的 Fork 按钮。图片来源:GitHub 官方文档。

下面假设原项目地址为 https://github.com/OWNER/PROJECT,你的 GitHub 用户名是 YOU。

第 1 步:Fork 并克隆自己的仓库

1
2
git clone https://github.com/YOU/PROJECT.git
cd PROJECT

第 2 步:关联原作者仓库

1
2
3
git remote add upstream https://github.com/OWNER/PROJECT.git

git remote -v

此时一般有两个远程仓库:

  • origin:你自己的 Fork,有权限向这里推送。
  • upstream:原作者仓库,用于获取最新代码。

第 3 步:从原项目目标分支创建新分支

1
2
git fetch upstream
git switch -c fix/api-timeout upstream/main

如项目规定以 develop 为目标分支,应从 upstream/develop 创建分支。

第 4 步:修改、测试、提交和推送

1
2
3
4
5
6
7
8
9
# 完成修改和项目要求的检查后
git status
git diff
git add path/to/changed.go path/to/changed_test.go
git diff --cached
git commit -m "fix: handle API timeout errors"

# 注意这里推送到自己的 Fork
git push -u origin fix/api-timeout

第 5 步:创建跨仓库 PR

在原项目的 PR 页面选择合适的仓库与分支(必要时点击 compare across forks):

GitHub compare across forks 入口真实截图

图 6:需要跨仓库提交时,点击 compare across forks。

GitHub 原仓库和目标分支选择官方截图

图 7:左侧 base repository / base branch 是代码准备合并到的目标。

GitHub Fork 来源仓库和来源分支选择官方截图

图 8:右侧 head repository / compare branch 是自己的 Fork 和开发分支。图片均来自 GitHub 官方文档。

1
2
3
4
base repository:    OWNER/PROJECT
base branch: main
head repository: YOU/PROJECT
compare branch: fix/api-timeout

提交前再次确认目标仓库是原作者仓库、来源是自己的 Fork,避免把 PR 提交给自己。

四、进阶:使用 GitHub CLI 在终端创建 PR

如果你习惯使用终端、Claude Code 或 Codex,可以安装 GitHub CLI(gh),无需每次打开浏览器。

Ubuntu 上可以先尝试通过系统包管理器安装:

1
2
sudo apt update
sudo apt install gh

然后登录并确认状态:

1
2
gh auth login
gh auth status

在已经提交并推送的开发分支上执行:

1
2
3
4
gh pr create \
--base main \
--title "fix: handle API timeout errors" \
--body "修复 API 请求超时处理,并补充相关测试。"

如果提交信息和 PR 内容已经准备妥当,也可以使用:

1
gh pr create --base main --fill

Fork 场景或同时存在多个远程仓库时,先检查当前仓库上下文,并在需要时明确指定目标仓库,例如:

1
gh pr create --repo OWNER/PROJECT --base main --head YOU:fix/api-timeout

常用的后续命令:

1
2
3
gh pr status       # 查看 PR 状态
gh pr checks # 查看 CI 检查
gh pr view --web # 在浏览器中打开 PR

也可以直接使用交互式 gh pr create,由 CLI 引导填写信息。完整选项参见 GitHub CLI 官方文档。

五、PR 提交后,如何处理审核意见?

维护者可能要求你调整代码或补充测试,这时通常不需要重新开一个 PR。

1
2
3
4
5
6
7
8
9
10
flowchart TD
A[Pull request opened] --> B[Automated CI checks]
B --> C[Maintainer review]
C --> D{Changes requested?}
D -->|Yes| E[Edit the same branch]
E --> F[Commit and push]
F --> B
D -->|No| G{CI passed?}
G -->|No| E
G -->|Yes| H[Merge pull request]

流程示意:在原开发分支提交新 Commit 并 Push,即可更新同一个 PR。

在 PR 页面切换到 Files changed,可以查看文件差异、调整 Diff 展示方式并处理审查意见:

GitHub Pull Request Files changed 代码审查界面截图

图 9:GitHub PR 的 Files changed 页面及 Diff 视图设置。图片来源:GitHub 官方文档。

保持在提交 PR 的原分支,继续修改、检查,然后提交并推送:

1
2
3
4
5
6
git switch fix/api-timeout

# 按照审核意见修改代码并完成测试
git add path/to/changed.go path/to/changed_test.go
git commit -m "test: cover timeout edge cases"
git push

新的 Commit 会自动出现在现有 PR 中。检查 CI 结果、回复审核评论,确认所有必要检查和审查通过后,再依照项目权限和规范合并。

如果开发期间目标分支有新提交,先与团队确认使用 Merge 还是 Rebase。对已经共享的分支执行 Rebase 后,可能需要强制推送;不要不加判断地对团队共用分支使用 git push --force。

六、常见问题与排查

1. git push 提示 Permission denied

常见原因包括:没有目标仓库的写入权限、SSH 密钥或 HTTPS 凭据未配置、远程地址指错仓库。先检查:

1
2
git remote -v
gh auth status

如果是开源项目且没有写入权限,应使用 Fork 流程,而不是尝试直接推送到原项目。

2. GitHub 页面上没有 Compare & pull request

可以手动进入 Pull requests → New pull request。检查分支是否成功推送、是否正确选择 base/head,以及两个分支之间是否确实存在可比较的提交。

3. PR 显示合并冲突

说明你的分支与目标分支的部分改动无法直接自动合并。先读取项目贡献指南,确认允许的同步方式;然后从目标仓库获取更新,选择合适的 Merge 或 Rebase 策略解决冲突。处理冲突后运行相关测试,再提交/推送。

4. PR 的 CI 检查失败

打开 PR 的 Checks 页面查看失败任务与日志,判断是代码错误、依赖问题、测试环境故障还是仓库原有的问题。修复后推送到同一分支,新的检查通常会自动触发。

5. PR 里出现大量无关文件

提交前检查 git diff、git diff --cached 和 git status。如果误把无关文件暂存了,可以使用 git restore --staged <文件路径> 将其移出暂存区;这不会删除工作区中的修改。若已经提交并推送,应谨慎处理历史,尤其是与他人共享的分支。

七、提交 PR 前的检查清单

  • [ ] 已阅读项目的 README.md / CONTRIBUTING.md / PR 模板。
  • [ ] 已确认目标仓库和目标分支,基于正确分支创建开发分支。
  • [ ] 修改范围聚焦,不包含无关文件或敏感信息。
  • [ ] 已按项目要求完成格式化、构建、测试和静态检查。
  • [ ] 已逐项检查 git diff 与暂存区变更。
  • [ ] Commit 信息清晰,开发分支已成功推送。
  • [ ] PR 的 base/head 方向正确,标题、修改说明、验证过程完整。
  • [ ] 已检查 CI 结果,并准备根据 Review 反馈继续更新。

八、速查:最常用的 5 条命令

适用前提:你有目标仓库的写入权限;当前已经在更新后的目标分支上;已经完成代码修改和必要测试,并且已登录 GitHub CLI。

1
2
3
4
5
6
git switch -c feat/new-feature
# 修改代码并检查后,仅添加本次 PR 需要的文件
git add path/to/changed-file
git commit -m "feat: add new feature"
git push -u origin feat/new-feature
gh pr create --base main --fill

如果你没有原仓库写入权限,先 Fork,再将分支推送到自己的 Fork,并把 PR 的 base 指向原作者仓库。

参考资料