Git and GitHubgit 2.54 · GitHub · macOS, Linux or Windows
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
26 small wins to finish your pathNext lesson →

Pull requests

A pull request is a proposal on GitHub to merge one branch into another, with a place to discuss it first. It is a GitHub feature, not a git command.

Last updated: 12 Oct, 2026 · git 2.54

Cloning a repository and the lessons before it covered every command that touches a repository. A pull request adds the one thing commands cannot: somebody else looking at the change before it lands.

A pull request from branch to merged. On your computer you run git switch -c prices-fix and then git push -u origin prices-fix. On GitHub you open a pull request, it collects review and comments, and it is merged into main. After the merge the branch is deleted, and back on your computer git switch main followed by git pull brings the merged work down. The pull request is a request on GitHub, not a git command, and nothing merges until someone merges it.

The name is a request to the project to pull your branch in. GitHub's docs put one rule on it: “Pull requests can only be opened between two different branches”. The branch you want merged is the compare branch; the branch it should land in is the base.

Pushing the branch the pull request is about

Everything before the pull request is git, and you have seen all of it. Make a branch, commit on it, push it with -u. The repository this pushes to is a bare repository in a folder on the same machine, standing in for the GitHub URL so the commands below are real runs. On GitHub the only difference is the URL.

ExampleThe stand-in, with main pushed to it
export GIT_AUTHOR_NAME=Reader GIT_AUTHOR_EMAIL=reader@example.com GIT_AUTHOR_DATE=2026-10-12T10:00:00
export GIT_COMMITTER_NAME=Reader GIT_COMMITTER_EMAIL=reader@example.com GIT_COMMITTER_DATE=2026-10-12T10:00:00
git init --bare -q ask-docs.git            # the stand-in for the repository on GitHub
mkdir ask-docs && cd ask-docs && git init -q -b main
printf 'Answer only from the notes.\n' > prompt.txt
printf 'chunk_size = 400\n' > settings.py
git add . && git commit -q -m "Add the prompt and the settings"
git remote add origin ../ask-docs.git && git push -u origin main

That is the starting point: one commit on main, in your copy and in the stand-in. The pull request is going to be about a branch that does not exist yet.

Committing on a branch and pushing it

ExampleThe branch is on the remote; nothing has been merged
git switch -c shorter-chunks
printf 'chunk_size = 200\n' > settings.py
git commit -q -am "Try 200 character chunks"
git push -u origin shorter-chunks
  • The branch was created on the remote. * [new branch] again, this time for shorter-chunks.
  • main has not changed. The branch sits beside it. Nothing merges until someone decides to merge it.

The two branches the pull request page compares

ExampleBoth branches, on the remote
git branch -r

git branch -r lists the branches the remote has. Both are there, and a pull request is a request to join one to the other.

Opening the pull request

This part needs a real GitHub account, so it is not run here. GitHub documents the web steps: open the repository, use the Branch menu to pick the branch with your commits, click the Compare & pull request banner above the file list, set the base dropdown to the branch you want to merge into and the compare dropdown to your branch, write a title and a description, and click Create Pull Request. For work that is not ready, the dropdown beside it offers Create Draft Pull Request.

The GitHub CLI does the same from the terminal.

bash
gh pr create --base main --head shorter-chunks \
  --title "Try 200 character chunks" \
  --body "Smaller chunks, same prompt. Opening for review."

gh pr create --fill     # take the title and body from the commit
gh pr create --web      # fill it in on the website instead

--base is the branch the code should end up in and --head is the branch holding the commits, which defaults to the branch you are on. --fill takes the title and body from the commit, and --draft opens it as a draft.

Choosing how the merge happens

GitHub offers three merge buttons, and each leaves a different history behind. All three have a git command that does the same thing, so you can see the difference before you press anything.

bash
setup() {                                   # a main with one commit, and a two-commit branch
  mkdir "$1" && cd "$1" && git init -q -b main
  printf 'chunk_size = 400\n' > settings.py
  git add . && git commit -q -m "Add the settings"
  git switch -q -c shorter-chunks
  printf 'chunk_size = 300\n' > settings.py && git commit -q -am "Try 300"
  printf 'chunk_size = 200\n' > settings.py && git commit -q -am "Try 200"
  git switch -q main
  printf 'Answer only from the notes.\n' > prompt.txt
  git add . && git commit -q -m "Add the prompt"
}

The function makes a fresh folder with the same history every time it is called: a main with one commit of its own, and a branch with two. Each block below calls it for its own folder, so the three merges can be compared on identical input.

ExampleThe history before any of the three buttons is pressed
cd ~ && setup merge-commit
git log --oneline --all --graph

Two commits on the branch, one of your own on main after the split. The three blocks below each end that picture differently.

Merge pull request

ExampleBoth commits are kept, joined by a merge commit
cd ~ && setup merge-commit
git merge --no-ff shorter-chunks -m "Merge pull request #1 from shorter-chunks"
git log --oneline --graph

Squash and merge

ExampleThe two commits arrive as one
cd ~ && setup squashed
git merge --squash shorter-chunks
git commit -q -m "Try 200 character chunks (#1)"
git log --oneline --graph

Rebase and merge

ExampleThe commits are replayed on to main
cd ~ && setup rebased
git switch -q shorter-chunks && git rebase main
git switch -q main && git merge --ff-only shorter-chunks
git log --oneline --graph
  • Merge commit keeps everything. GitHub's docs say it “preserves the full commit history from the pull request branch”. Both attempts are in the history and the graph forks and rejoins.
  • Squash leaves one commit. The docs describe it as combining “all commits in the pull request into a single commit on the base branch”. The two attempts are gone, and the line is straight.
  • Rebase leaves both commits, in a straight line. The docs call it adding “each commit onto the base branch without a merge commit, for a linear history”. The hashes are new ones, because a commit with a different parent is a different commit.
Merge commitSquash and mergeRebase and merge
Commits added to mainall of them, plus a merge commitoneall of them, rewritten
History shapeforks and rejoinsstraightstraight
Can you still read the steps?yesno, only the summaryyes
Good fora branch whose steps mattera branch full of small fixesa tidy history with no merge noise

Finishing up after the merge

Once the pull request is merged, the work is on the base branch on GitHub and not on your machine. A pull brings it down, and the branch that carried it can then go.

ExampleThe merge the button does, run here
cd ~ && git clone -q "$HOME/ask-docs.git" merge-button && cd merge-button
git merge -q --no-ff origin/shorter-chunks -m "Merge pull request #1 from shorter-chunks"
git push origin main          # stands in for someone pressing Merge pull request

The work is on main in the stand-in now. Your own copy knows nothing about it, which is the state you are in every time somebody merges your pull request.

Bringing the merge down to your copy

ExampleBack to main, and up to date for real
cd ~/ask-docs
git switch main
git pull

Switching said up to date, and it was wrong. Your copy had not heard about the merge yet, so it compared against what it last knew. The git pull on the next line is what told it, and the merge arrived as a fast-forward.

Clearing the branch away

ExampleThe branch deleted in both copies
git branch -d shorter-chunks
git push origin --delete shorter-chunks
git branch -a
  • git branch -d is the safe delete. It refuses if the branch holds commits that are not merged anywhere. -D deletes it anyway, which is what you want for an experiment you are throwing out.
  • Deleting it locally leaves it on the remote. git push origin --delete <branch> removes it there, which is what GitHub's delete button does.
  • Nothing is lost by deleting a merged branch. The commits are in main. A branch name is a label, not the work.

Where a pull request earns its keep

  • Any change to a repository other people depend on. A second reader catches the key left in a file and the comment that no longer matches the code.
  • A change you want a record of. The description, the discussion and the commits stay attached to the merge for ever.
  • Your own solo project. Opening one on yourself gives you the whole change on one page before you merge it, which reads differently from the editor.
Watch out. A pull request shows the difference between your branch and the base branch as the base branch is now. If the base moves while the pull request is open, the difference changes under you. Pull the base branch into yours, fix any conflicts on your branch and push again, so the reviewer reads something that merges cleanly.
Try it yourself
  • In the merge example, swap --no-ff for a plain git merge and read the graph. Git fast-forwards when it can, and the branch disappears from the picture.
  • Change the squash example's commit message to list both attempts, then run git show --stat HEAD to see what one commit now contains.
  • After the rebase example, run git log --oneline on both branches. The hashes match, because --ff-only moved the label rather than copying anything.

You understood something today that you didn't yesterday.