Chapter 8 of 10 · Git and GitHub for Beginners — Complete Free Tutorial
Pull Requests and Code Review
A push puts your code on GitHub; a pull request puts it in front of people. This eighth chapter of the Git and GitHub for Beginners series covers the pull request (PR) — the proposing, discussing and reviewing ritual that keeps shared codebases healthy — plus the automated checks that now run alongside every review.
Why pull requests exist
The name is historical: you are asking the repository to pull your changes in. In practice a PR is a conversation about a proposed change. It gives your work a title, a description, a line-by-line diff, a comment thread, a record of approvals, and a place for automation to report. Merging directly to main takes seconds but skips all of that; opening a PR takes two minutes and catches the bug your own eyes stopped seeing an hour ago.
The GitHub flow, step by step
GitHub flow is the small-team default, and you already know every Git command it uses:
- Create a branch from an up-to-date
main. - Commit your changes in focused, reviewable steps.
- Push the branch with
git push -u origin <branch>. - Open a pull request on GitHub, describing what and why.
- Respond to review comments and let checks run.
- Merge the PR and delete the branch.
Each pull request is effectively a request to merge one branch into another, and it stays open until someone merges or closes it.
Anatomy of a good pull request
Reviewers skim, so front-load the information they need:
| Section | Write this |
|---|---|
| Title | The outcome in a few words: “Fix broken footer links on mobile” |
| Summary | What changed and why, in two or three sentences |
| How to test | The exact steps a reviewer should follow to verify the change |
| Screenshots | Before and after for anything visual |
| Related work | Links to the issue, ticket or earlier discussion |
Keep pull requests small. A 15-line PR gets a careful review in five minutes; a 900-line PR gets a thumbs-up nobody means.
Reviewing well
Code review is a transfer of understanding, not a hunt for mistakes. Look for correctness first (does it do what the PR claims?), then clarity, then style — and keep comments specific and kind: suggest an alternative rather than only pointing at a problem. As the author, treat every comment as a question to answer, not a verdict to argue, and push follow-up commits rather than rewriting history that reviewers have already read. GitHub gives you three verdicts: comment for observations, request changes to block a real problem, and approve to signal the change is ready.
Checks, Actions, and how to merge
Modern repositories run automation on every PR. GitHub Actions can install dependencies, run your test suite, lint the code and build the project, showing a green tick or a red cross beside each commit. A PR with failing checks should never be merged, and branch protection can make that a rule instead of a courtesy. When everything passes, choose a merge strategy that suits your history:
| Option | Resulting history | Best for |
|---|---|---|
| Create a merge commit | All commits preserved plus a merge node | Keeping the full story of a feature |
| Squash and merge | The whole PR becomes one commit on main | Noisy branches full of “wip” commits |
| Rebase and merge | Your commits replayed in a straight line | Teams that want linear history without merge nodes |
git switch -c fix-footer-links
git commit -am "Fix the broken footer links"
Expected output:
[fix-footer-links 3f4e5d6] Fix the broken footer links
1 file changed, 2 insertions(+), 2 deletions(-)
git push -u origin fix-footer-links
Expected output:
Enumerating objects: 9, done.
To github.com:sara-bakery/bakery-site.git
* [new branch] fix-footer-links -> fix-footer-links
branch 'fix-footer-links' set up to track 'origin/fix-footer-links'.
gh pr create --fill
Expected output:
Creating pull request for fix-footer-links into main in sara-bakery/bakery-site
https://github.com/sara-bakery/bakery-site/pull/42
The gh command-line tool automates everything the web interface does — but reading the PR page yourself a few times first is the best way to understand what it creates.
Common mistakes
- Opening a PR from an out-of-date branch — merge or rebase on
mainfirst so the diff shows only your work. - Describing the diff instead of the intent — reviewers can read the diff; they cannot guess why you took this approach.
- Pushing commits that fix review feedback with
--force— ordinarygit pushis enough; force-pushing rewrites what reviewers already approved. - Ignoring failing CI checks — a green tick is not decoration; merging red code breaks
mainfor everyone. - Reviewing a 40-file PR in one sitting — ask the author to split it; nobody reviews a wall of changes carefully.
Practice checklist
- Push a feature branch to GitHub with
git push -u origin <branch>. - Open a pull request and fill in summary, test steps and a screenshot or before/after note.
- Read your own diff on the “Files changed” tab and fix one thing you would comment on.
- Leave a review comment on a teammate's PR (or on your own second PR) that suggests, not just points.
- Merge using a different strategy than the default and inspect the resulting commit graph with
git log --graph --oneline.
What's next?
Review sometimes reveals that history needs fixing — a stray file, a botched message. Continue to Correcting History: Reset, Revert and Rebase to learn which tools are safe and when.
Frequently Asked Questions
Git is a distributed version control system: it records snapshots of your project over time, lets you jump back to any point, branch off to try ideas safely, and merge other people's work. It is the industry standard — nearly every developer job assumes basic Git fluency — and it is the best insurance you can have against lost or broken code.
Git is the tool that runs on your machine and stores history locally; it works fully offline. GitHub is one of many websites (GitLab, Bitbucket) that hosts Git repositories in the cloud and adds collaboration features: pull requests, code review, issues and CI. You can use Git forever without GitHub — GitHub needs Git to be useful.
A repository (repo) is your project folder plus a hidden .git directory containing the entire history: every commit, branch and tag. It can be local (only on your computer) or remote (cloned on GitHub). Deleting .git loses the history but keeps your files; deleting the files while keeping .git leaves history with nothing to show.
git add moves changed files into the staging area — your draft of the next snapshot — so you can review exactly what will be recorded. git commit writes the staged snapshot permanently with a message describing why the change happened. Two steps by design: stage deliberately, commit intentionally.
A branch is a movable pointer to a commit — creating one costs almost nothing. Use a branch for every unit of work (feature, fix, experiment): it keeps unfinished code out of main, lets you review changes in a pull request, and makes abandoning a bad idea as simple as deleting the branch.
It depends on where the change lives. Unstaged edits: git restore file. Staged but uncommitted: git restore --staged file. Committed but not pushed: git reset --soft to keep changes staged. Already pushed: git revert commit — it writes a new commit that undoes the old one instead of rewriting shared history. The undoing chapter covers each case with commands.
A conflict happens when two branches changed the same lines and Git cannot choose automatically. Your file gets marked up with <<<<<<<, ======= and >>>>>>> sections; you edit it down to the correct result, remove the markers, then git add and git commit. Conflicts are normal, small and entirely fixable — they are not an error state.
A pull request (PR) is GitHub's proposal to merge one branch into another. It shows the diff, runs checks, and lets teammates comment line by line before the code lands. It is the centre of modern collaboration: work on a branch, open a PR, review, approve, merge.
Write the subject in imperative mood, under about 50 characters: "Add email validation to signup". Explain why, not what, in the body when it is not obvious. Many teams use Conventional Commits (feat:, fix:, docs:) so tools can generate changelogs — the workflows chapter shows the format in practice.
Secrets (API keys, .env files), dependencies (node_modules), build output (dist), and operating-system junk (.DS_Store, Thumbs.db). A .gitignore file excludes them from the start; the common mistakes chapter shows what to do if something already slipped into history.