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 main or develop).
  • 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.

Fork-and-Pull: Why You Request a Pull Instead of Pushing Your Fork full push access — it's yours feature-branch Original Repo maintainer's — no write access for you main git push — blocked 1 · open pull request 2 · maintainer pulls it in the contributor asks; the maintainer runs the actual pull
You can push freely to your own fork, but the original repo is off limits — a pull request is the ask that gets a maintainer to pull your commits in instead.

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.

A Commit Is a Git Object; a PR Is a Platform Record Commit a real git object hash: 1a2b3c4 author: Jane Doe timestamp: 2026-08-24 parent: prior commit tree: file snapshot Pull Request a platform database record base: main head: feature/retry-logic commits: a1b2c3d, 9f8e7d6… comments: 12 checks: 3 of 3 passing a pull request references commits — it never stores or replaces them
A commit is real git data with a hash and a parent; a pull request is metadata the platform keeps in its own database, pointing at commits rather than replacing them.

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:

  1. Branch — create a feature branch off the base branch (git checkout -b feature-name).
  2. Commit — make your changes, committing in whatever increments make the history readable later.
  3. Push — send the branch to the remote so the platform can see it; pushing a branch works the same mechanically whether the target is main or a feature branch, only the name changes.
  4. Open the PR — pick a base and a head branch, write a title and description, request specific reviewers.
  5. Checks run automatically — CI builds, tests, linters, and any other required status checks the repository has configured.
  6. Review — reviewers read the diff, leave comments, request changes, or approve.
  7. Iterate — push more commits to the same branch; they append to the same PR without opening a new one.
  8. 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.
The Pull Request Pipeline, Start to Finish 1 2 3 4 5 6 7 Branch Commit Push Open PR CI Checks Review Merge iterate: push more commits onto the same PR no new pull request opens for follow-up commits
Review and iterate is the loop that repeats — pushing more commits after feedback lands on the same pull request, not a new one, until checks pass and merge finally runs.

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:

  1. Push the branchgit push -u origin feature-name from the command line, or commit directly on GitHub if the change is small enough for the web editor.
  2. 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.
  3. 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.
  4. Write a title and description. Referencing an issue number — "Closes #42" — in the description auto-links it and closes it on merge.
  5. 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.

Same Object, Different Name Per Platform GitHub Pull Request GitLab Merge Request Bitbucket Pull Request base branch head branch same diff, same review, three different button labels
Three platforms, three buttons, but underneath each one is the same base-branch-vs-head-branch comparison — only the name and the color change.

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.

Two-Dot vs Three-Dot: What Each Diff Actually Includes M merge base X1 X2 base Y1 Y2 Y3 head Two-Dot Diff base..head M X1 X2 Y1 Y2 Y3 = every change on either branch since M Three-Dot Diff base...head M X1 X2 Y1 Y2 Y3 = only what's unique to head since M
Two-dot diff compares branch tips directly and drags in whatever changed on the base branch too; three-dot diff — what GitHub's PR view actually uses — starts from the merge base and shows only what's unique to your branch.

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.

Check Your Own Diff Before You Open the PR $ git diff origin/main...HEAD @@ -12,7 +12,9 @@ - return retry(count) + return retry(count, delay) - console.log('debug') select the output, copy it return retry(count) console.log('debug') return retry(count, delay) pasted in, side by side before opening the PR
Run the diff locally first, paste it into a side-by-side viewer, and catch the stray console.log or accidental revert before a reviewer ever sees it.

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.