Skip to content

The pull request flow

docs.scrimba.com

You have been working on a feature on your own branch, and it is ready. The project is shared: other people commit to it too, so you do not push straight to main and hope for the best. You want the change reviewed, discussed if needed, and merged in a way the rest of the team can see. That whole path, from a branch on your machine to code landing in main, is the pull request flow, and it is how almost every team on GitHub gets changes in. The Git side of it uses only commands from earlier chapters, a branch, a commit, and a push:

bash
$ git switch -c add-five-day-forecast
Switched to a new branch 'add-five-day-forecast'
$ git add src/forecast.js
$ git commit -m "Add five-day forecast panel"
$ git push -u origin add-five-day-forecast
Branch 'add-five-day-forecast' set up to track 'origin/add-five-day-forecast'.

That push sends your branch to GitHub. Nothing has merged yet, and nothing has even been proposed for review. The next step, opening the pull request, happens on GitHub's website.

What happens when you open a pull request

On GitHub, after a push like the one above, you will usually see a banner offering to compare your new branch against main. Click it, write a title and a short description of what changed and why, and open the page it creates. That page is a pull request, often shortened to PR: a request to merge one branch into another, with a running conversation attached to it.

A pull request proposes a merge and waits for approval. Someone, maybe you, maybe a teammate, reads the change, leaves comments, and only merges it once it looks right. Until that happens, your branch and main stay exactly as they were before you opened the page.

You do not have to wait until a feature is finished to open the PR. Many teams open one early and mark it as a draft, which signals "not ready for review yet" while still giving reviewers a place to comment on the direction before the work is done. A good description links any related issue and says what you tested, so a reviewer does not have to reconstruct that from the diff alone.

GitHub keeps a ref for every pull request on the remote, even before you add the PR's branch to your own repository: refs/pull/<number>/head. Fetch it directly to check out a teammate's PR on your machine, without adding their fork as a remote first.

bash
$ git fetch origin pull/42/head:pr-42
From github.com:mara-chen/weather-app
 * [new ref]         refs/pull/42/head -> pr-42
$ git switch pr-42
Switched to branch 'pr-42'

Now pr-42 is a real local branch built from pull request number 42, ready to run and test before you leave a single comment.

JunoWhat happens when you open a pull request A pull request is a request to merge your branch into another one, opened on GitHub after you push. Someone reviews the change first, and only then does it merge. The everyday loop is branch, push, open a pull request, get reviewed, merge.
JunoWhat happens when you open a pull request A PR proposes a merge and holds the conversation around it, and you can open one early as a draft to get feedback before the work is finished. Write the description like a reviewer will actually read it: what changed, why, and what you tested.
JunoWhat happens when you open a pull request GitHub keeps a hidden ref for every pull request, refs/pull/<number>/head, so you can fetch a teammate's PR straight onto your machine with git fetch origin pull/42/head:pr-42 instead of adding their fork as a remote. Test the change locally before you leave your first comment.

Fork vs branch

Opening a pull request from a branch works when you have write access to the repository, which is the normal case on your own team's projects. Contributing to a project you do not have write access to needs one extra step: a fork, your own copy of the repository under your own GitHub account.

You branch and commit inside your fork exactly as you would in a project you own, push to your fork, and then open the pull request from your fork's branch back to the original repository. GitHub compares across the two repositories the same way it compares two branches inside one repository, so the review and merge flow looks identical either way.

If you plan to send more than one PR to a project you have forked, it pays to add the original repository as a second remote, usually named upstream, while origin keeps pointing at your own fork. Run git fetch upstream and merge upstream/main into your branch to catch up with a project that has moved on since you forked it, using the same steps the Remotes and GitHub chapter covers for any remote.

JunoFork vs branch Branch directly when you have write access to a repository, your own team's projects. Fork when you do not: a fork is your own copy of someone else's repository, and you branch and push inside that copy before opening the pull request back to the original.
JunoFork vs branch A fork is a full copy of a repository under your account, used to contribute where you have no write access. You branch, commit, and push inside your fork like normal, and GitHub compares your fork's branch against the original repository the same way it compares two branches inside one project.
JunoFork vs branch Keep two remotes on a forked project: origin for your own fork, upstream for the original repository, so you can fetch and merge its changes as it moves forward. The PR itself still compares your fork's branch against the original repository's base branch.

Getting your pull request through review

Once a PR is open, it becomes a running conversation about the diff. A reviewer reads the change, leaves comments on specific lines, and either approves it or requests changes. When changes are requested, keep committing and pushing to the same branch: every push updates the same PR instead of creating a new one. Once a reviewer approves and any required checks pass, the PR is ready to merge.

GitHub tracks a review as one of three states: approved, changes requested, or commented (feedback with no verdict either way). Reply to comments as you address them and mark the conversation resolved so a reviewer can see at a glance what is left. If you push a fix for a specific comment, it helps to say so in your commit message or a reply, since a reviewer working through a long PR is scanning for exactly that.

How many approvals a PR needs, and whether every comment thread has to be marked resolved before the merge button unlocks, is usually a rule attached to the base branch itself on a well-run project.

JunoGetting your pull request through review A reviewer comments on your pull request and may ask for changes. Commit and push more work to the same branch to address it, and the PR updates itself automatically. Merging happens once someone approves.
JunoGetting your pull request through review Reviews land in one of three states: approved, changes requested, or commented. Push fixes to the same branch to update the PR, reply to comments as you resolve them, and keep the reviewer oriented in a long thread.
JunoGetting your pull request through review Review states drive whether the merge button unlocks, but the exact bar, how many approvals, whether every comment thread must be resolved, is usually a rule attached to the base branch itself on a well-run project.

Why a pull request won't merge

Sometimes the merge button on a PR is greyed out, and GitHub shows a message in its place: "This branch has conflicts that must be resolved" or "This branch is out of date with the base branch." Both messages describe a normal, fixable state: main has moved since you branched, usually because someone else's PR merged in the meantime, and your branch needs to catch up before GitHub can combine the two cleanly. This happens on any project with more than one contributor, so expect it rather than fear it.

Fix it locally the same way you would bring any two branches together:

bash
$ git switch add-five-day-forecast
$ git fetch origin
From github.com:mara-chen/weather-app
   9f8e7d6..b7c9e21  main       -> origin/main
$ git merge origin/main

If nothing overlaps, the merge completes on its own and you push, and the PR updates and the button unlocks. If the same lines changed on both sides, Git marks the conflict in the file for you to resolve exactly as covered in Merging and conflicts: open the file, edit out the conflict markers to the result you want, then git add and git commit to finish the merge, and push again.

A stuck pull request needs catching up or its conflict resolved.

On a PR that stays open for more than a day or two, it pays to merge main into your branch every so often rather than waiting for the button to block you. A branch that is a day out of date usually merges with no conflict at all; one that is three weeks out of date has a lot more surface for two changes to collide on the same lines. GitHub's own "Update branch" button on the PR page does this same merge for you when there is nothing to resolve by hand.

Some teams rebase a feature branch onto main instead of merging it, to keep the eventual history linear rather than showing a merge commit for every PR. That trade-off is the same one covered in Merging and conflicts: rebasing rewrites your branch's commits, which is safe as long as you are the only one with copies of them, so a solo feature branch that only lives on your machine and its PR is a reasonable candidate. Rebasing a branch other people have already pulled and built on top of is not, since it rewrites history they are relying on.

JunoWhy a pull request won't merge A greyed-out merge button means your branch is behind main or has a conflict with it. Nothing is broken. Switch to your branch, fetch, and merge main in, resolving any conflict the same way you would anywhere else, then push again.
JunoWhy a pull request won't merge Merge main into a long-running branch every so often instead of waiting for GitHub to block the merge button. Smaller, more frequent catch-ups mean smaller conflicts, and GitHub's "Update branch" button can do the no-conflict case for you.
JunoWhy a pull request won't merge Merging or rebasing your branch onto main both fix a stuck PR, and a solo feature branch is safe to rebase since nobody else has a copy of its commits. Once other people have pulled a branch, stick to merging it instead.

What a pull request is, under the hood

A pull request is not a Git object, and there is no git command that creates one. Git and GitHub are not the same thing: Git only knows about commits, branches, and other refs (a ref is a name pointing at a commit). GitHub builds the whole PR page, the comments, and the merge button on top of two refs it compares: your branch (the head) and the branch you are merging into (the base, usually main). That comparison is why you open a pull request on GitHub's website (or through GitHub's own command-line tool or API), and never with a plain git command.

Because a PR lives on GitHub, separate from the repository's history, its comments and review conversation do not travel with the commits. If you ever move a project to a different host, every commit comes with you unchanged, but the pull requests and their discussions stay behind on GitHub, since they were never part of the Git data in the first place.

GitHub computes a PR's diff by finding the merge base, the most recent commit both the head ref and the base ref share, and comparing the head against that point rather than against whatever main looks like right now. Each push to the branch adds a new version to the same PR rather than creating a new comparison. Branch protection is the policy layer sitting on top of all this: a rule you (or your organization) attach to a branch, most often main, that can require passing status checks, require a set number of approving reviews, and block direct pushes so every change is forced through a pull request. Git itself enforces none of this. It will let you push straight to main from your terminal right up until GitHub's rule refuses the request.

JunoWhat a pull request is, under the hood A pull request is a GitHub feature layered on top of Git: Git tracks commits and branches, and GitHub adds the PR page and the merge button by comparing two of them. Keeping Git and GitHub apart in your head explains a lot of what otherwise looks like magic.
JunoWhat a pull request is, under the hood A PR compares your branch against a base branch and lives entirely on GitHub, so its comments and review history do not move if the repository ever changes host. The commits themselves are the only part that is really Git.
JunoWhat a pull request is, under the hood A PR is GitHub comparing a head ref against a base ref from their shared merge base, updated with each push. Branch protection is the enforced policy on top, requiring checks or reviews before merge. Git itself enforces none of this: it will let you push straight to main until GitHub's rule stops it.