教程

Pull request 是什么:如何在 GitHub 上创建 PR

更新于: · Skillaria.Top

Pull request(PR)是请求把一个分支合并到仓库主分支的申请:你推送分支、发起 PR,团队查看差异、评论并运行自动检查,获得批准后再合并。在 GitLab 中它叫 merge request(MR)。它是平台的功能,不是 Git 命令,与 git pull 没有关系。

Pull request(PR)是一项将一个分支的修改合并到另一个分支的提议,它在 GitHub 等平台上发起。你把分支推送到服务器,打开 PR,团队查看差异、逐行评论、运行自动检查,然后才点击合并按钮。在 GitLab 中,同样的机制叫作 merge request(MR)。如果简单回答「git pull request 是什么」:它是「我写完代码」与「代码进入 main」之间的一个控制节点。

Git pull request 是什么,名称从何而来

这个名称是历史形成的。作者请仓库的所有者把他的分支「拉取」(pull)过去,于是叫 pull request。GitLab 选择按结果命名,叫 merge request,即合并请求。Bitbucket 和 Azure DevOps 使用 pull request 这个说法。它们本质上是同一件事:源分支、目标分支、描述、讨论、检查,以及合并按钮。

需要明确的是:pull request 不属于 Git 的一部分。Git 中没有「PR」这种对象,你无法通过提交创建它,也无法在 .git 文件夹中找到它。它是平台的概念:GitHub 保存描述、评论和检查状态,而 Git 只负责 PR 中包含的分支和提交。

在开发者的日常交流中,PR 几乎总是指 pull request:「发个 PR」「PR 等待评审」「合并了那个 PR」。说到 GitLab,则通常说 MR。

Pull request 与 git pull:区别在哪里

这两个概念因为名字相似而经常被混淆。git pull 命令从远程仓库下载新提交,并立即合并到当前分支,它是终端里的本地操作。而 pull request 是 GitHub 等网站上的一个页面,用来提议把一个分支合并到另一个分支。

git pullPull request
位于何处终端中的 Git 命令GitHub、GitLab、Bitbucket 上的页面
作用把服务器上的修改拉取到你的电脑提议把你的分支合并到他人的或共享的分支
参与者只有你作者、评审人、CI
是否需要平台不需要,只需要 remote需要

Git 中并没有「git pull request」这个单独的命令。有一个古老的工具 git request-pull,但它只是生成一封带有修改摘要的邮件文本,与 GitHub 上的 PR 无关。

为什么需要 pull request

项目只有一个人维护时,可以直接提交到 main。一旦仓库里有两个或更多人,直接推送到主分支就会导致构建失败和工作丢失。Pull request 同时解决了几个问题:

  • 代码评审:第二个人在修改进入 main 之前就能看到,并发现作者遗漏的错误;
  • 自动检查:GitHub Actions 或其他 CI 会为每个 PR 运行测试和 lint,检查失败时可以阻止合并有问题的代码;
  • 决策记录:一年以后,通过 PR 不仅能知道改了什么,还能知道为什么改,以及当时的讨论;
  • 为他人项目做贡献:在开源项目中你没有写入权限,PR(来自 fork)是提交修复的唯一途径;
  • 分支保护:GitHub 的分支保护规则可以禁止直接推送到 main,并要求经过批准的 PR。

如何在 GitHub 上从分支创建 pull request

这是团队中所有人都有仓库写入权限时的主要流程。工作在功能分支中进行,main 只通过 PR 更新。

第 1 步:基于最新的 main 创建分支

git switch main
git pull
git switch -c feature/login-form

分支名最好直观:feature/…、fix/…,或者带上任务编号。评审人一眼就能知道内容是什么。

第 2 步:提交修改

git add src/login.js
git commit -m "添加登录表单"

一个 PR 只做一件事。100 到 300 行的小 PR 可以快速而仔细地评审,而 3000 行的 PR 则会让评审人不经细看就批准。

第 3 步:把分支推送到服务器(为 pull request 执行 git push)

git push -u origin feature/login-form

-u 参数把本地分支与远程分支关联起来,之后只需普通的 git push。GitHub 的响应中会附上创建 PR 的链接:

remote: Create a pull request for 'feature/login-form' on GitHub by visiting:
remote:      https://github.com/team/app/pull/new/feature/login-form

第 4 步:在网页界面中打开 PR

点击终端中的链接,或者打开 GitHub 上的仓库,刚推送的分支旁边会出现「Compare & pull request」提示框。另一种方式是进入「Pull requests」标签页,点击「New pull request」。

  1. 检查分支对:base 是合并的目标(通常是 main),compare 是你的分支。
  2. 填写标题:说明做了什么,用祈使语气或简短描述。
  3. 在描述中说明为什么做、如何检查,以及哪些内容不在本次范围内。如果仓库中有 PR 模板,字段会自动填入。
  4. 在右侧指定评审人(Reviewers)、负责人以及标签(如团队使用)。
  5. 点击「Create pull request」,或选择草稿(见下文)。

在终端中创建 PR:GitHub CLI

如果不想切换到浏览器,可以使用官方工具 gh。它不是 Git 命令,而是 GitHub 自己的程序,但处理 PR 很方便:

gh pr create --base main --title "添加登录表单" --body "Closes #42"
gh pr list
gh pr checkout 57
gh pr merge 57 --squash

关于「git 有哪些 pull request 命令」,诚实的回答是:Git 负责分支、提交和推送,而 PR 本身是在平台上创建的,可以通过浏览器、gh 或 API 完成。

如何通过 fork 向他人的仓库提交 pull request

在开源项目中你通常没有写入权限。因此,你要先 fork,也就是在 GitHub 上创建一份仓库副本,在副本中工作,然后把修改提交给原项目。

  1. 在项目页面点击「Fork」,你就会得到 github.com/你的用户名/project。
  2. 克隆你的 fork,并把原项目添加为第二个 remote,通常命名为 upstream。
  3. 创建分支,提交修改,把分支推送到你的 fork。
  4. 在 GitHub 上打开 PR:base repository 选原项目,head repository 选你的 fork 和你的分支。
git clone git@github.com:you/project.git
cd project
git remote add upstream https://github.com/owner/project.git
git switch -c fix/typo-readme
git commit -am "修正 README 中的拼写错误"
git push -u origin fix/typo-readme

开始之前,请先阅读 CONTRIBUTING.md 文件:其中通常说明了提交信息的格式、测试要求,以及是否需要先创建 issue。保持 fork 与原项目同步:在你的分支中执行 git fetch upstream 和 git rebase upstream/main,让 PR 不落后于原项目。

GitLab 中的 merge request:是什么,如何创建

GitLab 的 merge request 与 GitHub 的 pull request 完全对应。界面和细节有所不同,但流程相同:分支、推送、合并请求、评审、合并。

创建 MR 有几种方式:

  • 通过 git push 推送新分支后,终端中打印的链接;
  • 在项目的 Merge requests 部分点击「New merge request」,选择源分支和目标分支;
  • 推送时直接使用 GitLab 的 push options,无需进入浏览器。
git push -o merge_request.create -o merge_request.target=main origin feature/login-form

GitLab 的术语略有不同:批准称为 approve,负责合并的人称为 assignee,而自动化使用的是 GitLab CI/CD 和 .gitlab-ci.yml 文件,而不是 GitHub Actions。GitLab 同样有草稿,称为 Draft。

评审 pull request

评审人打开「Files changed」标签页查看差异。可以在任意一行留下评论,或提出可直接应用的修改建议(suggestion),作者点击一次即可采纳。评论会被收集到一次评审中,并附上以下结论之一一起提交:

  • Comment:仅提出意见,不作决定;
  • Approve:批准修改;
  • Request changes:需要继续修改,不建议合并。

好的评审关注逻辑、可读性、测试和边界情况,而不是争论个人喜好。格式问题最好交给 CI 中的 linter 处理。作者应当回复每一条评论:说明已修改、已讨论,或保持原样并给出理由。

如何在本地检查他人的 PR(git checkout pull request)

有时需要在自己的电脑上运行代码。如果分支位于同一个仓库中,git fetch 加 git switch 就够了。对于来自 fork 的 PR,GitHub 提供了特殊的引用 pull/<编号>/head:

git fetch origin pull/57/head:pr-57
git switch pr-57

GitLab 中对应的引用叫作 merge-requests/<编号>/head。如果安装了 GitHub CLI,也可以直接执行 gh pr checkout 57。

如何根据评审意见更新 pull request

PR 与分支绑定,而不是与某个具体提交绑定。只需在同一分支上提交新的修改并推送,PR 就会自动更新,无需重新创建。

git commit -am "根据评审意见修改"
git push

Git rebase pull request:拉取最新的 main

PR 评审期间,main 可能继续向前推进。如果 GitHub 提示分支已过期或存在冲突,请更新分支。使用 rebase 可以保持线性历史:

git fetch origin
git rebase origin/main
# 解决冲突,然后 git add,git rebase --continue
git push --force-with-lease

rebase 之后提交的哈希值会改变,因此普通的 push 会被拒绝,需要强制推送。使用 --force-with-lease:如果分支上出现了你不知道的他人提交,它会拒绝覆盖。如果这个分支不只由你一个人使用,更安全的做法是用 git merge origin/main 合并 main,这不会改写历史。

如何合并 pull request:merge、squash 与 rebase

评审通过、检查全部变绿后,就可以合并 PR。GitHub 提供三种方式,仓库管理员可以只开放其中一部分。

方式main 中的结果适用场景
Create a merge commit分支的全部提交加一个 merge 提交需要完整历史,并希望看出哪些提交是一起加入的
Squash and merge一个包含 PR 全部修改的提交分支中有很多「fix typo」之类的小提交,希望 main 的日志干净
Rebase and merge分支的提交逐个加入,没有 merge 提交每个提交都整洁,并且本身就有意义

通过网页界面合并 PR,本质上就是 git merge,只是在服务器上执行。合并后 GitHub 会提示删除分支,同意即可,因为分支已经没有用了。然后在本地清理:

git switch main
git pull
git branch -d feature/login-form

也可以在终端中手动合并:把分支合并到本地的 main 并推送。GitHub 会识别出分支的提交已经进入目标分支,并自动把 PR 标记为 merged。但这样会跳过检查和分支保护规则,所以团队中一般使用按钮合并。

Auto-merge 与 merge queue

如果检查耗时较长,可以开启自动合并:所有条件满足后,PR 会自动合并。在大型仓库中,还会使用合并队列(merge queue),它把 PR 放在队列中已有的 PR 之上进行检查,避免 main 因互不兼容的修改而损坏。

Pull request 草稿(Draft)

草稿是尚未准备好评审的 PR。它不能被合并,评审人也不会收到评审请求。草稿适合尽早展示方向、运行 CI,或收集架构方面的意见。在 GitHub 上创建 PR 时,可以在创建按钮的下拉菜单中选择草稿,或执行 gh pr create --draft。工作完成后,点击「Ready for review」。在 GitLab 中,草稿通过在 MR 标题前加上 Draft: 前缀来标记。

Pull request 与 issue 的关联

如果在 PR 描述中写上关键字和 issue 编号,合并到默认分支后,GitHub 会自动关闭该 issue:

Closes #42
Fixes #17
Resolves #8

close、fix、resolve 的各种形式(closes、fixed、resolves)都有效。只写 #42 而不加关键字,只会生成一个链接,不会关闭任务。GitLab 中,Closes #42 及类似写法同样有效。

能否删除 pull request 或 merge request

普通成员无法删除 GitHub 上的 PR,只能通过「Close pull request」按钮关闭它。关闭的 PR 会保留在历史中,可以重新打开。关闭后请单独删除分支。彻底删除 PR 只能联系 GitHub 支持,例如当 PR 中包含了密钥时。

在 GitLab 中,作者或具有相应权限的成员可以关闭 MR,而删除通常只有项目所有者或管理员可以做。如果 PR 中包含了密码或令牌,仅仅关闭是不够的:要立即撤销这个密钥,因为提交已经在服务器上了。

Pull request 的常见错误

  • 从 main 发起 PR。你把修改提交到了自己的 main,然后从它发起 PR。下一个 PR 会包含之前的修改。请始终在单独的分支中工作。
  • base 分支选错。PR 的目标是 develop 而不是 main,或者反过来。可以点击 PR 标题旁的 Edit 按钮修改 base。
  • 差异中混入无关文件。构建产物、node_modules、.env。把它们加入 .gitignore,并用 git rm --cached 从索引中移除。
  • PR 过大。把任务拆分为多个有先后顺序的小 PR。
  • rebase 之后直接普通推送。出现 rejected (non-fast-forward) 错误。应使用 git push --force-with-lease,而不是 --force。
  • 冲突处理不当。不要在网页编辑器中盲目解决冲突。应在本地拉取 main,解决冲突并运行测试。

git flow 等流程中的 pull request

Pull request 可以融入任何团队流程。在 GitHub Flow 中一切都很简单:从 main 创建分支,发起 PR,评审,合并,部署。在 git flow 中,PR 从功能分支指向 develop,发布分支和热修复分支也要通过 PR 合并到 main。在基于主干的开发中,PR 很短,存活时间以小时计,而不是以天计。无论采用哪种流程,原则都是一样的:对共享分支的修改只能通过 pull request 进入。

总结

Pull request 是平台的机制,而不是 Git 命令:它是推送到服务器上的分支,加上用于讨论、评审和检查的页面。在 GitLab 中叫 merge request,口语中则说 PR 和 MR。请从独立的分支创建 PR,保持它小巧,通过推送更新同一分支,并根据项目历史选择合并方式。Fork、功能分支和 pull request 在本课程的 Git 实践中都有详细讲解。

常见问题

一个 pull request 需要几位评审人?

通常一到两位就够了。最少批准人数在分支保护规则中设置,未达到要求时合并按钮不可用。

没有 fork 也能创建 pull request 吗?

可以,只要你对仓库有写入权限:直接在仓库中创建分支,然后从它发起 PR。只有在没有写入权限时才需要 fork。

PR 长时间无人评审怎么办?

可以在评论或团队聊天中礼貌地提醒评审人。在开源项目中,等待可能需要几周;请先检查 PR 是否符合 CONTRIBUTING.md 的要求。

能否在同一个 fork 的两个分支之间打开 pull request?

可以。在比较页面中,base 和 head 都选择你自己的 fork。这样在把修改提交给原项目之前,可以先评审自己的改动。

为什么 PR 中显示了我没有做过的提交?

很可能分支不是基于最新的 main 创建的,或者选错了 base 分支。请对最新的 main 执行 rebase,或修改 PR 的 base。

通过视频学习本主题

课程“Git 从零基础到胜任开发者”结合实践详细讲解了这一主题。

前往课程