Insights·2026-08-27

What is the difference between a PR and an MR?

A PR (Pull Request) and an MR (Merge Request) are the same feature under two names. GitHub, Bitbucket, Gitea and Azure DevOps call it a Pull Request; GitLab calls it a Merge Request. Everywhere the procedure is the same: show the difference between two branches, collect review, merge. What is easy to miss is that Git itself has a pull request. The command git request-pull genuinely exists. All it does, though, is print a summary to standard output asking someone to take your commits — there is no review screen, no approve button, no open or closed state. It was built to be pasted into a mailing list. So the PR or MR you open every day is not that text; it is the web object the hosting service layered on top of Git. The real difference is not the name but what each service attached to that object, and you can read it straight off the terminal. Approval on GitHub is one flag on a review, while on GitLab it is a standalone command with separate ways to list eligible approvers and to revoke an approval. CI status is the other way round: GitHub attached it to the PR, GitLab kept pipelines in a separate layer. When a team moves between the two platforms, the document that needs rewriting is not the glossary — it is the approval policy.

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

What is the difference between a PR and an MR?

As soon as you start writing code with other people you hear it: "I opened a PR." Or "could you review my MR?" Change jobs and find that everyone calls MR what you used to call PR, and you pause for a second, wondering whether what you learned still applies.

The short answer is that they are the same thing. GitHub calls it a Pull Request and GitLab calls it a Merge Request. Bitbucket, Gitea and its fork Forgejo, and Azure DevOps all sit on the Pull Request side. Either way the job is identical: display the difference between your branch and the target branch, let colleagues comment on that difference, and merge once everyone agrees.

The names diverged because they describe the same event from opposite ends. Pull is the receiving side's action — please pull my branch in. Merge is the result of that request — please merge it. You can still see the older shape in the tooling: the help text for gh pr create offers to fork the base repository for you. The picture of contributing to somebody else's repository is baked into the word.

In practice the two words are interchangeable and nobody misunderstands. Say "I opened a PR" on a GitLab team and everyone knows exactly what you mean. Terminology is rarely the actual problem.

PlatformWhat it is calledURL path
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 has a pull request too — git request-pull

A terminal showing the commands to run git request-pull and read its usage, with a note that it has no review screen, no approve button and no state — it only prints to standard output.

Here is the part most people miss. Git itself has a pull request. Not as a metaphor: there is a command with that name.

Open a terminal and type git request-pull. With no arguments it prints its usage. Give it a starting point and a repository URL and it assembles a summary of the commits you have made since that point, asking someone to take them. In the documentation's own words, it generates a request asking your upstream project to pull changes into their tree.

And that is the whole of it. What the command produces is text on your screen. No review screen appears. There is no approve button, no open or closed state, nowhere to leave a comment, no record of who signed off. It is not even sent anywhere — it goes to standard output.

That is exactly how it was meant to be used. In mailing-list development of the kind the Linux kernel still practises, you copy this output into an email and send it to a maintainer. The maintainer reads it and runs git pull themselves. Review happens in the replies.

Try it yourself
# Confirm the command exists (it prints usage)
git request-pull

# The real shape
git request-pull <start> <repository-URL> [<end>]

# Example: ask someone to take your branch's commits since main
git request-pull main https://github.com/me/repo.git my-branch

# Full documentation
git help request-pull

So what is the PR or MR you use every day?

Put the two previous sections together and the answer falls out. The screen you open every day is not a Git feature. It is a web object that a hosting service layered on top of Git.

Git's job ends at computing the difference between two branches. Drawing that difference as a web page, letting people comment line by line, recording who approved, attaching automated checks, and enabling the merge button only when the conditions are met — all of that belongs to products like GitHub and GitLab. That is why a PR or MR has a number, an open or closed state, assignees and labels. Git has none of these concepts.

Why does the distinction matter in practice? Because local Git knowledge alone tells you nothing about your team's collaboration rules. However well you know git merge, how many approvals your team needs before a merge lives in platform settings, not in Git. And it is precisely those settings that are shaped differently on GitHub and GitLab.

Where the real difference shows — gh pr versus glab mr

The fastest way to see the difference between the two platforms is to put the subcommand lists of their official CLIs side by side. What a product treats as a first-class concept shows up directly in its command structure.

In GitHub CLI, approval is one flag on a review: gh pr review --approve. Approving, commenting and requesting changes are all options of the single review command. In GitLab CLI, approval is a command of its own. There is glab mr approve, and next to it approvers to list who is eligible and revoke to take back an approval you already gave.

CI status runs the other way. GitHub attached pipeline results to the PR itself: gh pr checks prints that PR's check results. GitLab has no glab mr checks. Instead there is a separate top-level glab ci layer where pipelines live.

A few other commands have a distinct flavour. GitLab has glab mr rebase to replay the source branch on top of the target, and glab mr for to open an MR straight from an issue. GitHub has gh pr update-branch to bring a branch up to date, gh pr revert to undo a merged PR, and gh pr ready to mark a draft as ready for review.

What you want to doGitHub (gh)GitLab (glab)
Creategh pr createglab mr create
Listgh pr listglab mr list
Check out locallygh pr checkoutglab mr checkout
View the diffgh pr diffglab mr diff
Approvegh pr review --approveglab mr approve
Revoke an approval(submit a new review)glab mr revoke
List eligible approvers(no dedicated command)glab mr approvers
See CI statusgh pr checksglab ci (separate layer)
Mergegh pr mergeglab mr merge
Compare the subcommand lists yourself
# GitHub CLI
gh pr --help

# GitLab CLI
glab mr --help

# See where approval lives
gh pr review --help      # it is the --approve flag
glab mr approve --help   # it is its own command

Opening your first one — on both GitHub and GitLab

Two stacked terminals placing the GitHub PR commands beside the GitLab MR commands, showing that install, login, push, create and merge follow the same symmetric order on both.

The claim that only the name differs is easiest to verify by running the commands. The sequence below is perfectly symmetric: swap pr for mr and gh for glab and you are on GitLab.

First install the CLI and log in. On macOS both come from Homebrew. Logging in is gh auth login and glab auth login respectively; a browser opens to authenticate your account. You only do this once.

After that it is ordinary Git work. Create a branch, commit, push it to the remote. Nothing differs between GitHub and GitLab here, because this part is pure Git.

Finally you open the PR or MR. Both CLIs support --fill, which pulls the title and body out of your commit messages. If it is not ready for review yet, add --draft. A draft opens with the merge button locked, which is what you want when you are sharing work in progress.

Once it is open you can inspect it without leaving the terminal. Add --web to open it in a browser; run plain view and the title, body and state print in the terminal.

Opening a PR on GitHub
# One-time setup
brew install gh
gh auth login

# The work
git switch -c fix-login
git commit -am "Fix the message shown on failed login"
git push -u origin fix-login

# Open the PR (title and body filled from commits)
gh pr create --fill

# Open it as a draft
gh pr create --fill --draft

# Check it, then merge
gh pr view
gh pr checks
gh pr merge --squash
Opening an MR on GitLab (same sequence)
# One-time setup
brew install glab
glab auth login

# The work
git switch -c fix-login
git commit -am "Fix the message shown on failed login"
git push -u origin fix-login

# Open the MR (title and body filled from commits)
glab mr create --fill

# Open it as a draft
glab mr create --fill --draft

# Check it, then merge
glab mr view
glab mr approve
glab mr merge

The document you actually have to rewrite when you switch

So when a team moves from GitHub to GitLab or the other way, where does the work actually land? Not in the glossary. Saying MR instead of PR takes about a day to get used to.

What needs rewriting is the approval policy. How many approvals are required before a merge. Whether a code owner's approval is mandatory. Whether a new commit invalidates existing approvals. Whether an approval can be withdrawn. These rules live under different names and on different screens on the two platforms. The CLI structure above is a summary of that gap: the fact that GitLab gives eligible-approver listing and approval revocation their own commands tells you how thickly the concept is modelled inside the product.

One more thing to settle at the same time: the merge strategy. Whether you squash commits into one by default, rebase then merge, or keep a merge commit — every team picks one, and that setting also has a different name and location on each platform. While you are migrating, write it down too.

To sum up: a PR and an MR are the same object. What differs is not the name but the rules attached to that object, and those rules are held by the platform, not by Git.