A pull request — PR for short — is a request to merge one branch's commits into another, opened on a hosting platform like GitHub, GitLab, or Bitbucket rather than through git itself. It bundles a diff, a discussion thread, and a merge button around commits that already exist; git has no idea a pull request exists at all — the concept lives entirely on top of git, in the hosting platform's own database.
Strip away the review comments, the CI checks, and the approve button, and what's left is a diff between two branches — the entire PR workflow is really a way of formalizing the process of diffing two files, at the scale of a whole branch instead of one file. That's the thread this guide follows: what a pull request actually is, how to open one from the browser or the terminal, how GitLab and Bitbucket use the same idea under different names, and — because almost nothing else written about pull requests spends any real time on it — how to actually read the diff a PR puts in front of you, before you ever click merge.
Whether you searched what is a pull request, what is a git PR, how to make a pull request, or just typed "pr request" into a search bar half-remembering the term, the mechanics underneath are the same handful of ideas repeated with different button labels depending on the platform.
Quick Answer: What a Pull Request Actually Is
A pull request means exactly what its two words imply: a request that the maintainer of a repository pull in changes you — or a teammate — made on a separate branch. Practically, a PR is:
- A named branch comparison — a head branch (yours) proposed against a
base branch (usually
mainordevelop). - A diff — every line added, removed, or changed between the two, computed from where the branches share a common ancestor.
- A discussion thread attached to that diff, where reviewers comment line by line or in general.
- A gate — most teams require passing CI checks and at least one approval before the merge button unlocks.
- A merge action — once approved, the PR's commits (or a squashed or rebased version of them) join the base branch.
None of that is native to git. What is a git PR, precisely? It's a GitHub-, GitLab-, or Bitbucket-specific object stored in that platform's own database, referencing commits that do live in git. Delete your account on the platform and the commits survive in every clone that already has them; delete the platform's copy of the PR itself and only the review history, comments, and metadata disappear — commits that were already merged stay exactly where they are.
Pull Request Means: A Proposal, Not a Push
Pushing a commit with git push writes directly to a branch — if you have write access,
the branch simply changes, no approval required. A pull request means something more specific: a
proposal that the branch change, submitted for someone else to accept or reject. That's the entire
reason the workflow exists — it inserts a review step between "I finished this code" and "this code
is now part of the shared branch," and it does that without requiring the author to have write
access to the branch being changed at all.
Git what is a pull request answers itself once you separate git's job from the platform's: git tracks commits, branches, and their history — nothing about approval or discussion. Everything that makes a PR a PR — the comment threads, the "Approve" button, the required-checks gate, the merge button itself — is UI and database rows the hosting platform layers on top of two git objects it already understood: a base branch, and a head branch carrying commits that aren't on the base yet. The same split trips people up on git meaning versus GitHub: one is a program that runs on your machine, the other a website that hosts what the program produces.
Why It's Called a Pull Request and Not a Push Request
The literal answer: because the person opening it usually can't push. In the fork-and-pull model GitHub built its entire workflow around, an outside contributor forks a repository, pushes commits to their own copy, and has no write access whatsoever to the original — pushing directly isn't an option. What they can do is ask the maintainer to pull those commits in. Opening pull requests on repos you don't have write access to is exactly this ask, formalized into a UI: a request for someone else to run the pull, rather than something you push yourself.
The name has real lineage, too, not just a story about permissions. GitHub's "pull request" descends
directly from git's own git request-pull command (covered in full further down) — a
tool that existed years before GitHub did, used to ask a maintainer to pull from a public branch.
GitHub built a web UI and a review workflow around the same underlying idea and kept the name.
GitLab, arriving slightly later with a different design philosophy, named its version after the
action that finishes the process instead of the action that starts it — hence merge request, not
pull request, even though the two features do essentially the same job.
PR vs Commit: What Each One Actually Is
PR vs commit is worth answering precisely, because the two sit at completely different layers. A commit is a real git object — a snapshot of the repository's tree at one point, with a hash, an author, a timestamp, and a pointer to its parent commit or commits. It exists the moment you run the git commit command, whether or not anyone ever sees it, whether or not it's ever pushed anywhere. Commits are what git's entire history is made of.
A pull request is not a git object at all. It's metadata the hosting platform keeps in its own
database: a reference to a base branch, a reference to a head branch, a title, a description, the
list of commits currently reachable on the head but not the base, comments, review state, and check
results. Nothing about a PR lives in the .git directory — clone a repository and every
commit comes with it; nothing about its pull request history comes along unless you separately query
the platform's API.
Practically: one PR usually wraps several commits, not one. You can have ten commits and zero PRs — never pushed, or pushed without ever opening one — or a single commit and a single PR for a tiny fix. The commit is the unit git actually stores and diffs; the PR is the unit your team reviews and merges.
The Pull Request Workflow, Start to Finish
Every platform's version of this follows the same shape, GitHub/GitLab/Bitbucket labeling aside:
- Branch — create a feature branch off the base branch
(
git checkout -b feature-name). - Commit — make your changes, committing in whatever increments make the history readable later.
- Push — send the branch to the remote so the platform can see it;
pushing a branch works the same mechanically whether
the target is
mainor a feature branch, only the name changes. - Open the PR — pick a base and a head branch, write a title and description, request specific reviewers.
- Checks run automatically — CI builds, tests, linters, and any other required status checks the repository has configured.
- Review — reviewers read the diff, leave comments, request changes, or approve.
- Iterate — push more commits to the same branch; they append to the same PR without opening a new one.
- Merge — once checks pass and required approvals are in, the PR merges (as a merge commit, a squash, or a rebase, depending on team convention) and the branch is usually deleted.
Step seven is where most of the actual time on a PR gets spent, and it's also where keeping the branch current matters: if the base branch has moved on since you branched, rebasing your branch onto the latest base — or merging the base into your branch — keeps the diff reviewers see limited to just your changes, instead of a tangle of unrelated updates that landed on main after you started.
How to Make a Pull Request on GitHub (Web UI)
Making a pull request on GitHub, without touching a terminal, is five steps once the branch is pushed:
- Push the branch —
git push -u origin feature-namefrom the command line, or commit directly on GitHub if the change is small enough for the web editor. - GitHub shows a "Compare & pull request" banner on the repository's main page right after a push to a non-default branch — click it. Missed the banner? The Pull Requests tab → "New pull request" gets you the same form.
- Pick the base and compare branches. GitHub defaults the base to the repository's default branch, which is usually right; the compare branch is your feature branch.
- Write a title and description. Referencing an issue number — "Closes #42" — in the description auto-links it and closes it on merge.
- Click "Create pull request." Add reviewers, labels, or a project from the sidebar before or after — none of it is required to open the PR itself.
That's the full answer to how to make PR on GitHub without the CLI. The web UI also supports opening a PR across forks — the compare branch dropdown lets you pick a different repository entirely, which is exactly the fork-and-pull flow the term "pull request" was named after.
Git Pull Request From the Command Line with gh
There is no git pull-request command built into git itself — git make a pull request
isn't something plain git can do, because a PR isn't a git concept, it's a platform one. What
actually handles a git pull request from the command line is GitHub's own CLI, gh,
which wraps the GitHub API in commands that feel like git.
# Install once, then authenticate
gh auth login
# From inside a branch with commits already pushed
gh pr create
Run without flags, gh pr create asks interactively for a title, body, and reviewers.
Skip the prompts with flags instead:
# Reuse the branch's commit messages as the title and body
gh pr create --fill
# Full control over every field
gh pr create --title "Add retry logic to api client" \
--body "Retries failed requests up to 3 times." \
--base main --head feature/retry-logic
# Open a draft instead of a ready-for-review PR
gh pr create --draft --base and --head pick the two branches being compared, exactly like the
web form's dropdowns; --fill saves typing a description by reusing your commit messages.
One thing worth knowing before you reach for it: --draft and --web are
mutually exclusive — you can open a draft, or open the form in a browser to finish it visually, not
both in the same command.
gh also closes the loop the other way — pulling someone else's PR down to test it
locally is gh pr checkout 123, which does the equivalent of
checking out a remote branch but resolves the PR
number to the right branch automatically, fork included. This is the closest thing to a git pull
request from terminal, start to finish: open with gh pr create, inspect with
gh pr diff or gh pr checkout, merge with gh pr merge.
Draft PRs on GitHub: Open Early, Signal Not-Ready
Opening a draft PR early is normal on GitHub, often the moment a branch exists, well before anything in it is finished. A draft is a regular pull request with one flag set: it can't be merged until someone explicitly marks it ready. Everything else works identically — CI runs, comments happen, commits push to the same branch — it's purely a signal to reviewers that the code inside isn't finished yet, so a drive-by approval doesn't accidentally green-light something still in progress.
# From the CLI
gh pr create --draft
# Later, once it's actually ready
gh pr ready
From the web UI, the "Create pull request" button on the compare screen has a dropdown next to it
offering "Create draft pull request" as the alternative. Converting a draft to a full PR is one
click — "Ready for review" — on the PR page itself, or gh pr ready <number> from
the terminal without opening a browser at all.
Draft PRs became free for every GitHub repository — public and private — in May 2025; before that, drafts were a private-repo feature gated behind a paid plan. If older material you've read says otherwise, that's stale — draft PRs are unrestricted on GitHub today, and they're worth using early: opening one the moment a branch exists, rather than waiting until the work is "done," gets CI and early feedback running in parallel with the rest of the implementation instead of after it.
What Is a Pull Request in GitLab? Merge Requests Explained
What is a pull request in GitLab, precisely, is a bit of a trick question — GitLab doesn't call it that. The same object — a proposed set of commits, a diff against a target branch, a review thread, a merge button — is a merge request (MR) there, and every GitLab doc, button, and API endpoint uses that name instead. If you've searched what is pull request GitLab and gotten merge request results back, that's not a mismatch — it's the answer.
GitLab's CLI, glab, mirrors gh's shape closely enough that switching
between them is mostly a vocabulary exercise:
# Authenticate once
glab auth login
# Open a merge request from the current branch
glab mr create
# Draft, with a title and target branch specified
glab mr create --draft --title "Add retry logic" --target-branch main
GitLab marks a draft two ways: the --draft flag above, or prefixing the MR's title with
Draft: directly — both do the same thing, and GitLab's UI shows the same "Draft" badge
and the same merge-blocked state either way. Functionally, an MR and a PR are the same feature with
a different name and a different button color; nothing else in this guide behaves differently just
because the platform is GitLab instead of GitHub.
Bitbucket, Gitea, and the git request-pull Original
Bitbucket uses the same term GitHub does — pull request, not merge request — so anyone moving between the two doesn't need to relearn vocabulary, just menu locations. Gitea, the smaller self-hosted alternative many teams run internally, also calls it a pull request and models the review flow closely on GitHub's.
None of that is where the phrase actually started, though. Long before GitHub existed, git shipped —
and still ships — a command called git request-pull, and it does something meaningfully
different from any platform feature above: it generates a plain-text summary of a commit range,
formatted for a maintainer to read in an email client, not a browser.
# Summarize what changed between v1.0 and your current HEAD,
# formatted as a request for a maintainer to pull
git request-pull v1.0 https://example.com/repo.git HEAD
The output is a shortlog and diffstat with a note asking the recipient to pull from the given URL at
the given ref — no server-side object gets created anywhere; it's just text you paste into an email.
This is the real git command pull request people sometimes go looking for and don't find, because it
behaves nothing like GitHub's feature: no comments, no CI, no merge button, nothing persistent. Full
flag-by-flag detail lives in git's
own request-pull documentation. It predates GitHub by years, and it's still exactly
how patches move through the Git project's own mailing list and much of the Linux kernel's
development, where contributors send either a request-pull summary pointing at a public
branch, or, for smaller changes, a generated patch file
attached directly to the email instead of a link to pull from.
A Pull Request Is a Diff: How to Actually Review One
Everything above — the base branch, the head branch, the comments, the checks — exists to frame one thing: a diff. Open any PR's "Files changed" tab and that's what you're actually looking at, hunk by hunk. Reviewing a pull request, stripped of the platform chrome around it, is a diff-reading skill, and it's the one piece of the workflow almost nothing written about pull requests spends real time on.
One detail worth knowing before you trust what a "Files changed" tab shows you: GitHub computes it
using three-dot semantics — base...head — not the two-dot, tip-to-tip diff you'd get
from comparing the branches directly. A three-dot diff shows only what changed on your branch since
it diverged from the base, even if the base has moved forward since then; a two-dot diff would also
include every change that landed on the base in the meantime, which is almost never what a reviewer
wants to see. The full mechanics of that distinction — what "diverged" means, how git finds the
merge base, when the two forms actually disagree — are covered start to finish in
the guide to diffing two files; the short version
here is enough to explain why a stale base branch can make a small PR's diff look confusingly large,
or hide changes you expected to see.
Reading the diff itself, at any size, comes down to a few habits. Read hunk headers —
@@ -12,7 +12,9 @@ — as coordinates, not noise; they tell you exactly where in the
file you are without re-reading everything above it. Separate whitespace-only and reformatting hunks
from logic changes before judging either one; most diff viewers, GitHub's included, have an "ignore
whitespace" toggle for exactly this reason. And for a PR large enough that the platform's own viewer
starts collapsing files or truncating hunks — which GitHub does past a certain diff size — pulling
the raw diff into a dedicated code review tool
built to handle large diffs without truncating them is usually faster than fighting the platform's
UI.
Compare Your Branch Before You Open the Pull Request
The diff a reviewer sees is the same diff you could look at yourself before opening the PR — and
doing that first catches the embarrassing stuff before anyone else does: a leftover
console.log, a file reverted by accident, a formatter that silently reordered half a
file. Checking is one command:
# Compare your branch against the base it'll target,
# using the same three-dot logic GitHub uses for the PR view
git fetch origin
git diff origin/main...HEAD
Running git fetch first matters — without it, origin/main in your local
repo is whatever it was last time you fetched, not what's actually on the remote right now;
the difference between fetch and pull covers why the two
aren't interchangeable here in more depth.
Reading that output in a terminal works, but it's not built for scanning — long lines wrap
awkwardly, and there's no way to fold context you've already checked. Diff Checker, the free
extension this site is built around, is a plain-text comparison tool, not a git client — it has no
idea what a branch or a commit is, and it doesn't talk to GitHub, GitLab, or any hosting platform's
API. What it does is take two blocks of text — pasted in, or uploaded as files, from .txt
and .js to .py, .json, and a long list of other common
formats — and render them in Monaco's editor, side-by-side or unified, with formatting differences
easy to isolate from real ones. Paste the output of git diff origin/main...HEAD into one
pane, or paste "before" and "after" versions of a specific file into the two panes directly, and scan
the result before you ever open the PR form.
It's manual — copy the diff out of your terminal, paste it in — not an integration, and there's an optional AI summary (bring your own OpenAI key) if you'd rather get a plain-English description of a large diff before reading it line by line. Nothing you paste in leaves the browser unless you turn that on. The same tool also runs with nothing to install, at diffchecker.pro/compare/, for a one-off check before a PR you don't want an extension for.
Comparison Table: Pull Requests Across Platforms
The same feature, five names and toolchains, side by side:
| Platform | Term Used | CLI Command | Draft Support | Diff Semantics |
|---|---|---|---|---|
| GitHub | Pull Request (PR) | gh pr create | Yes — free for all repos, public and private, since May 2025 | Three-dot: base...head, diff from the merge base |
| GitLab | Merge Request (MR) | glab mr create --draft | Yes — --draft flag or a Draft: title prefix | Diff from the merge base, same three-dot-style comparison |
| Bitbucket | Pull Request (PR) | No first-party CLI — web UI or the REST API | Yes — a Draft toggle on the create screen | Diff from the merge base |
| Gitea | Pull Request (PR) | Web UI, or the official tea CLI | Yes — WIP: or [WIP] prefix in the PR title | Diff from the merge base |
| Plain git, no host | None — a plain-text summary, not a stored object | git request-pull <start> <url> [<end>] | No concept of draft — you just don't send the email yet | Two-dot range: start..end, as a shortlog and diffstat |
Pull Request Best Practices That Reviewers Notice
A handful of habits separate PRs that get reviewed quickly from ones that sit for days:
- Keep it small. A 200-line PR gets a careful review; a 2,000-line PR gets a skim and an approval nobody fully means. Split unrelated changes into separate PRs even if they're both "done" at the same time.
- Write a title that states the change, not just the ticket number — "Fix retry logic dropping the last attempt" tells a reviewer more before they've opened the diff than a bare issue reference does.
- Review your own diff first. Opening the "Files changed" tab on your own PR before requesting anyone else catches a surprising number of stray debug lines and forgotten files — cheaper to fix silently than to have someone else flag it.
- Clean up the commit history before requesting review, not after. Squashing your commits into a small number of well-described ones — or dropping a stray "wip" commit with an interactive rebase — is far easier while the branch is still yours alone than once reworking history means coordinating a force-push with reviewers who've already started reading.
- Respond to every comment, even the ones you disagree with. A comment marked "resolved" with no reply reads as ignored, not addressed.
- Use draft state honestly. Opening a real PR before it's ready invites premature approval; opening a draft when it's actually finished just adds a click nobody needed.
Keeping a feature branch aligned with a fast-moving base is its own recurring chore — merging the base branch into yours partway through a long-lived PR avoids a painful, all-at-once conflict resolution the day you finally try to merge.
Frequently Asked Questions
How do I make a pull request?
Push your branch to the platform hosting your repository — GitHub, GitLab, or Bitbucket — then
either click the "Compare & pull request" prompt in the web UI, or run gh pr create
(GitHub CLI) or glab mr create (GitLab CLI) from the terminal. Either path has you
pick a base branch, a head branch, a title, and a description before submitting the PR for
review.
What is the purpose of a pull request?
A pull request creates a dedicated place for review and discussion before a set of commits merges into a shared branch. It lets reviewers comment on specific lines, lets CI run checks against the proposed change, and requires approval before the merge button unlocks — all before the code becomes part of the branch everyone else builds on.
What's the difference between a pull request and a merge request?
None functionally — "pull request" is what GitHub, Bitbucket, and Gitea call the same object
GitLab calls a "merge request" (MR). Both bundle one or more commits, a diff against a target
branch, a discussion thread, and a merge action; the name differs because GitHub's term traces
back to git's own request-pull command, while GitLab named its version after the
action that finishes the process instead.
Why is it called a pull request and not a push request?
Because the person opening it usually can't push directly to the target repository. In the fork-and-pull model, a contributor pushes their changes to their own fork and then requests that the maintainer pull those changes into the main repository — the maintainer runs the actual pull, not the contributor, which is what the name describes.
What is the difference between a PR and a commit?
A commit is a git object — a snapshot of the repository at one point, identified by a hash, existing whether or not anyone reviews it. A pull request isn't part of git's data model at all; it's the hosting platform's own record wrapping one or more commits with a diff view, a discussion thread, checks, and a merge button.