Insights·2026-08-27

PR 和 MR 有什么区别

PR(Pull Request)和 MR(Merge Request)是同一个功能,只是名字不同。GitHub、Bitbucket、Gitea 和 Azure DevOps 称之为 Pull Request,GitLab 称之为 Merge Request。无论在哪个平台,流程都一样:展示两个分支之间的差异,接受评审,然后合并。容易被忽略的是,Git 本身也有 pull request。git request-pull 这个命令是真实存在的。但它所做的只是把一段「请把这些提交拉过去」的摘要打印到标准输出,没有评审界面,没有批准按钮,也没有打开或关闭的状态。它本来就是为了粘贴进邮件列表而设计的。所以我们每天打开的 PR 和 MR 并不是那段文字,而是托管服务在 Git 之上搭建的网页对象。真正的差别不在名字,而在于各家服务往这个对象上挂了什么,这一点在终端里可以直接读出来。批准在 GitHub 上是评审命令的一个选项,在 GitLab 上则是独立命令,还另有查询可批准人和撤销批准的命令。CI 状态则相反,GitHub 把它挂在 PR 上,GitLab 把流水线放在独立层级。因此团队在两个平台之间迁移时,真正需要重写的不是术语表,而是批准规则文档。

PR과 MR은 같은 물건이다 — 이름만 다르고 규칙은 플랫폼이 쥔다. 나란히 놓인 두 터미널 창에 gh pr과 glab mr 라벨이 붙어 있고, GitHub은 Pull GitLab은 Merge, git request-pull은 요약문만 출력한다, 리뷰 화면·승인·상태는 플랫폼의 몫, gh pr을 glab mr로 바꾸면 그대로 된다는 네 줄이 함께 조판된 포스터

PR 和 MR 有什么区别

一旦开始和别人协作写代码,很快就会听到这样的话:「我提了个 PR。」或者「帮我看下这个 MR。」换了公司之后发现,原来叫 PR 的东西这里大家都叫 MR,难免会愣一下,怀疑自己学过的东西在这里是不是不管用了。

先说结论:它们是同一个东西。只是 GitHub 叫它 Pull Request,GitLab 叫它 Merge Request。Bitbucket、Gitea 及其分支 Forgejo、Azure DevOps 都在 Pull Request 这一边。无论哪一边,做的事情完全一样:把你的分支和目标分支之间的差异显示出来,让同事对着差异留言评审,达成一致之后按下按钮合并。

名字分道扬镳,是因为它们从相反的两端描述同一件事。pull 是接收方的动作,意思是「请把我的分支拉过去」。merge 是这个请求实现之后的结果,意思是「请合并它」。工具里至今还留着更早的形态:gh pr create 的帮助文本会提出替你 fork 目标仓库。向别人的仓库贡献代码这幅图景,就嵌在这个词里。

所以在实际工作中两个词可以混用,没有人会听不懂。在用 GitLab 的团队里说「我提了个 PR」,大家照样明白你的意思。术语本身几乎从来不是真正的问题。

平台叫法URL 路径
GitHubPull Request (PR)/pull/123
GitLabMerge Request (MR)/-/merge_requests/123
BitbucketPull Request (PR)/pull-requests/123
Gitea · ForgejoPull Request (PR)/pulls/123
Azure DevOpsPull Request (PR)/pullrequest/123

Git 本身也有 pull request — git request-pull

终端画面,展示确认 git request-pull 是否存在及查看用法的命令,下方说明它没有评审界面、批准按钮和状态,只会输出到标准输出。

这里有一个大多数人会漏掉的事实:Git 自己也有 pull request。不是比喻,是真的存在一个叫这个名字的命令。

打开终端敲 git request-pull。不带参数时它会打印用法。给它一个起点和一个仓库地址,它就会把你从那个起点之后做的提交整理成一段摘要,请对方把这些改动拉过去。用官方文档的说法,它生成的是一份请求上游项目把你的改动拉进他们仓库的说明。

然而这就是全部了。这个命令产出的只有屏幕上的文字。不会弹出评审界面,没有批准按钮,没有打开或关闭的状态,没有留言的位置,也不会记录谁批准过。它甚至不会被发送到任何地方,只是输出到标准输出而已。

因为它本来就是这么用的。在 Linux 内核那种基于邮件列表的开发方式里,你把这段输出复制到邮件正文里发给维护者。维护者读完之后自己执行 git pull。评审则通过邮件回复来进行。

自己动手试一下
# 确认命令存在(会打印用法)
git request-pull

# 实际形态
git request-pull <起点> <仓库URL> [<终点>]

# 例:请对方拉走你分支上 main 之后的提交
git request-pull main https://github.com/me/repo.git my-branch

# 完整文档
git help request-pull

那我们每天用的 PR 和 MR 到底是什么

把前面两节接起来,答案就出来了。我们每天打开的那个界面并不是 Git 的功能,而是托管服务在 Git 之上搭建的网页对象。

Git 的工作到计算两个分支之间的差异为止。把这个差异画成网页、允许逐行留言、记录谁批准过、挂上自动化检查、只有在条件满足时才让合并按钮可用 —— 这些全都是 GitHub、GitLab 这类产品的职责。所以 PR 和 MR 才会有编号,有打开和关闭的状态,有负责人和标签。这些概念在 Git 里一个都没有。

这个区分在实践中为什么重要?因为只靠本地的 Git 知识,你无法知道团队的协作规则。无论你多熟悉 git merge,你们团队需要几个人批准才能合并,这件事写在平台设置里,而不在 Git 里。而恰恰是这些设置,在 GitHub 和 GitLab 上长得不一样。

真正的差别在终端里显形 — gh pr 与 glab mr

看清两个平台差别最快的办法,是把两家官方 CLI 的子命令列表并排放在一起。一个产品把什么当作一等概念,会直接体现在它的命令结构里。

在 GitHub CLI 里,批准是评审命令的一个选项:gh pr review --approve。批准、留言、要求修改都是 review 这一个命令的选项。而在 GitLab CLI 里,批准本身就是一个独立命令。有 glab mr approve,旁边还有查询谁有批准资格的 approvers,以及撤回自己已给出批准的 revoke。

CI 状态则正好相反。GitHub 把流水线结果挂在 PR 上,敲 gh pr checks 就能看到这个 PR 的检查结果。GitLab 没有 glab mr checks 这样的东西,取而代之的是一个独立的顶层命令层 glab ci,流水线在那里处理。

还有几个命令的气质明显不同。GitLab 有把源分支重新落到目标分支之上的 glab mr rebase,以及直接从议题创建 MR 的 glab mr for。GitHub 有把分支更新到最新的 gh pr update-branch、回滚已合并 PR 的 gh pr revert,以及把草稿标记为可评审的 gh pr ready。

想做的事GitHub (gh)GitLab (glab)
创建gh pr createglab mr create
查看列表gh pr listglab mr list
拉到本地gh pr checkoutglab mr checkout
查看改动gh pr diffglab mr diff
批准gh pr review --approveglab mr approve
撤销批准(重新提交评审)glab mr revoke
查询可批准人(无专用命令)glab mr approvers
查看 CI 状态gh pr checksglab ci(独立层级)
合并gh pr mergeglab mr merge
自己对比子命令列表
# GitHub CLI
gh pr --help

# GitLab CLI
glab mr --help

# 看批准挂在哪里
gh pr review --help      # 以 --approve 选项出现
glab mr approve --help   # 以独立命令出现

第一次提交 — GitHub 和 GitLab 两边都试一遍

上下两个终端并排放置在 GitHub 提 PR 与在 GitLab 提 MR 的命令,显示安装、登录、推送、创建、合并的顺序在两边完全对称。

「只是名字不同、流程一样」这个说法,实际敲一遍命令就能立刻验证。下面的顺序在两边完全对称:把命令里的 pr 换成 mr、gh 换成 glab,就是 GitLab 的做法。

首先安装 CLI 并登录。在 macOS 上两者都可以用 Homebrew 安装。登录分别是 gh auth login 和 glab auth login,会打开浏览器完成账号认证。这一步只需要做一次。

接下来就是平时的 Git 操作:创建分支、提交、推送到远端。到这里为止 GitHub 和 GitLab 没有任何差别,因为这部分是纯粹的 Git。

最后创建 PR 或 MR。两边的 CLI 都支持 --fill 选项,会从提交信息里自动填好标题和正文。如果还没准备好接受评审,加上 --draft 以草稿形式提交。草稿打开时合并按钮是锁住的,适合提前分享还在进行中的工作。

提交之后不用打开浏览器,在终端里就能查看。加上 --web 会在浏览器中打开,只敲 view 则会把标题、正文和状态打印在终端里。

在 GitHub 上提 PR
# 首次设置
brew install gh
gh auth login

# 干活
git switch -c fix-login
git commit -am "修正登录失败时显示的提示"
git push -u origin fix-login

# 创建 PR(标题和正文自动取自提交)
gh pr create --fill

# 以草稿形式创建
gh pr create --fill --draft

# 查看后合并
gh pr view
gh pr checks
gh pr merge --squash
在 GitLab 上提 MR(顺序相同)
# 首次设置
brew install glab
glab auth login

# 干活
git switch -c fix-login
git commit -am "修正登录失败时显示的提示"
git push -u origin fix-login

# 创建 MR(标题和正文自动取自提交)
glab mr create --fill

# 以草稿形式创建
glab mr create --fill --draft

# 查看后合并
glab mr view
glab mr approve
glab mr merge

迁移时真正需要重写的文档

那么团队从 GitHub 迁到 GitLab,或者反过来,真正要动手的地方在哪里?不是术语表。把 PR 改口叫 MR,大概一天就习惯了。

需要重写的是批准规则文档:合并前需要几个人批准,代码所有者的批准是否必需,有新提交推上来时既有批准是否失效,批准能不能撤回。这些规则在两个平台上散落在不同的名字和不同的界面里。前面看到的 CLI 结构就是这个差距的摘要 —— GitLab 把查询可批准人和撤销批准都做成独立命令,说明这个概念在产品内部被建模得有多厚。

还有一件事值得一并确定:合并策略。默认把提交压成一个(squash)、先变基再合并、还是保留合并提交,每个团队都要选一个,而这个设置在各平台上的名字和位置也不一样。趁着迁移,把它一起写进文档。

总结起来是这样:PR 和 MR 是同一个东西。不同的不是名字,而是挂在这个东西上的规则,而这些规则握在平台手里,不在 Git 手里。