6.2 Pull Requests
Key Takeaways
- A pull request is a proposal to merge a head branch into a base branch so reviewers can discuss the change before it lands.
- GitHub organizes a pull request into Conversation, Commits, Checks, and Files changed tabs; a Findings tab may show automated review such as code scanning, and the merge box shows blockers separately from those tabs.
- Draft pull requests cannot be merged, and CODEOWNERS are not automatically requested until the draft is marked ready for review.
- Fork-and-pull lets anyone with read access (when forking is allowed) push to their own fork and open a pull request on the upstream repository; the shared-repository model uses topic branches inside one repo that collaborators can push to.
- Reviewers submit Comment, Approve, or Request changes; maintainers with write access then merge with a merge commit, squash and merge, or rebase and merge depending on how the team wants history to look.
Why pull requests are GitHub's key collaboration feature
After issues, GH-900 asks you to describe how to use pull requests. GitHub's About pull requests definition is the one to memorize: a pull request is a proposal to merge code (or any files) into a project so people can discuss and review changes before merging. Domain 1 already taught GitHub Flow—branch, commit, open a PR, review, merge, delete the branch. This Domain 3 skill is the product surface of that PR: the tabs, draft state, collaboration model, review outcomes, and merge methods.
A pull request always has a head (the branch that contains the work) and a base (the branch that will receive the work). On github.com the default base is usually the repository default branch (commonly main). Opening a PR does not change the base branch. GitHub creates temporary refs that point at the head and, when possible, at a simulated merge result so checks can run without touching main.
You need write permission on the repository to merge. Opening a pull request from a fork does not require write on the upstream repository; that is the point of the fork-and-pull model below.
The pull request tabs
GitHub groups the context reviewers need into tabs. Name them; GH-900 loves "which tab shows X?"
| Tab | What you see | Why it exists |
|---|---|---|
| Conversation | Description, timeline, comments, submitted reviews | The human discussion and the decision record |
| Commits | Each commit on the pull request branch, in order | How the head branch evolved; useful when commits are meaningful |
| Checks | Automated tests, builds, and other status checks | CI and required checks that can block merge |
| Files changed | The diff reviewers annotate | The actual proposed change |
| Findings | Automated review such as code scanning alerts on the proposed changes | Security and quality findings tied to this PR |
The merge status is not a fifth classic study-guide tab. It sits in the pull request header and in the merge box: missing approvals, failing checks, conflicts, draft state, and other blockers. Do not say "look on Commits to see if you can merge." Merge-readiness lives in the merge box.
Compare pages and pull request pages can show different diffs because they may choose different merge bases. If main moved after the PR opened, the PR page focuses on what the PR introduced; a compare view of two refs shows the current gap. That detail is easy to over-study. The exam-level point is: the Files changed tab on the pull request is the review surface, not a random compare URL.
Line comments and suggested changes live on Files changed and then appear in Conversation. A review is not complete until the reviewer submits it; pending comments are visible only to that reviewer.
Draft pull requests
When you create a pull request you can open it as a draft. GitHub is explicit:
- A draft cannot be merged.
- Code owners are not automatically requested to review a draft.
- Drafts exist so you can share work-in-progress without formally requesting reviews.
- When you mark it ready for review, GitHub requests reviews from any code owners.
- You can convert a ready pull request back to a draft at any time. People already subscribed to notifications are not automatically unsubscribed.
Search with draft:true or draft:false. A required-review branch protection rule still sees a draft as not mergeable; marking ready is what starts the real review clock. Exam trap: "drafts hide the pull request." They do not. They change mergeability and CODEOWNERS auto-request behavior.
Fork-and-pull versus shared repository
How you open pull requests depends on the collaborative development model.
Fork and pull
In the fork-and-pull model, anyone with read access can fork the upstream repository if the owner allows forks. You push to your fork, then open a pull request from your fork's branch to the upstream base branch. You do not need permission from upstream to push to your fork. Maintainers review and merge in the upstream repository. This is the default open-source pattern: low friction for first-time contributors, no write access on the canonical repo.
Optional upstream setting: allow people with push access on upstream to push commits onto your pull request branch ("Allow edits from maintainers"). That lets a maintainer fix a small CI miss without blocking on the contributor.
GitHub notes that a fork and its upstream share Git objects. Content you push to a fork is reachable from the upstream network. Do not treat a fork of a public project as a private vault.
Shared repository
In the shared repository model, collaborators already have push access to one repository. They create topic branches and open pull requests in that same repository. The PR is still the review and discussion step before merging into the main development branch. This is typical of small teams and private organization projects where everyone is already a collaborator.
| Question | Fork and pull | Shared repository |
|---|---|---|
| Where do you push? | Your fork | A topic branch in the shared repo |
| Do you need write on the canonical repo to propose a change? | No (read + fork) | Yes, at least push on a branch |
| Typical home | Public open source | Private teams |
| Pull request still used? | Yes, into upstream | Yes, into the default or other base branch |
GH-900 will give you a one-line story. "External contributor, no write, public repo" is fork-and-pull. "Five teammates, all have push, private product repo" is shared-repository. Pull requests exist in both; the difference is where the head branch lives.
Reviews: Comment, Approve, Request changes
Pull request reviews let people comment on changes, suggest exact edits, and then submit one of three review decisions:
| Decision | Meaning |
|---|---|
| Comment | General feedback; does not approve and does not request changes |
| Approve | The changes are ready to merge from this reviewer's point of view |
| Request changes | The author should address the feedback before merging |
Anyone with read access can review and comment. Requesting a review is stricter: you need write access (pull request authors who are owners or write collaborators can request; organization members with write or triage can also assign a reviewer). You can request a person or a team who has at least read access; they get a notification. If you request a team and code review assignment is enabled, GitHub requests specific members and removes the team as the reviewer.
CODEOWNERS automatically request review when a pull request (that is not a draft) touches owned paths. Administrators can add a branch protection or ruleset requirement that a pull request must be approved by a code owner before merge. Required reviews are a protection feature; they are not automatic on every public repository.
Exam traps on reviews:
- Comment is a real submitted review. It is not a failed approval.
- Request changes is not a merge. It is a blocker when reviews are required.
- A read-only outsider can leave a review on a public repository; they cannot merge.
- Assigning someone as assignee does not request their review. Reviewers and assignees are different sidebar fields (section 6.3).
Merge methods at conceptual depth
With write permission, and with the repository allowing the method, you pick how history should look. GitHub's merge docs are the source.
Merge commit
The default Merge pull request action adds all commits from the feature branch onto the base plus an explicit merge commit. GitHub uses git merge --no-ff. You keep the full topic-branch history and a visible merge point. Choose this when individual commits are meaningful or the team wants to see exactly when a branch joined main.
Squash and merge
Squash and merge combines every commit on the pull request into one commit on the base branch (fast-forward of that single squashed commit). Work-in-progress fixups disappear from default-branch history. Choose this when the PR is one logical change and the branch is noisy. The repository must allow squash merging. GitHub generates a default squash message you can edit; administrators configure whether that default includes the PR title, description, or commit list.
Squash is a poor fit for a long-running branch you keep reusing. Later pull requests from that same head can reintroduce already-squashed commits and replay conflicts. After a squash merge, delete the branch (GitHub Flow) and start fresh.
Rebase and merge
Rebase and merge replays each topic-branch commit onto the base without a merge commit, producing a linear history. GitHub always rewrites committer information and creates new SHAs, even in cases where local git rebase would not. It also drops originally empty commits (git commit --allow-empty), which local rebase keeps by default. If GitHub cannot rebase cleanly, you rebase locally, resolve conflicts, push, and try again. Choose rebase when commits are already clean and the team wants linear history without a merge bubble.
| Method | What lands on the base branch | History shape | Typical reason to choose it |
|---|---|---|---|
| Merge commit | All PR commits + a merge commit | Non-linear; merge node | Preserve complete history |
| Squash and merge | One combined commit | Linear, compact | One PR = one logical change |
| Rebase and merge | Each PR commit, new SHAs, no merge commit | Linear, detailed | Clean commits, no merge commit |
Indirect merges exist: if the head commits become reachable from the base through another path (another PR or a direct push), GitHub can mark this PR merged even if this PR's protection rules were not satisfied. Uncommon, but it explains a PR that shows merged when nobody pressed the button on that page.
Exam scenarios and traps
- "Share WIP without requesting code owners" → draft pull request, not a secret branch nobody can see.
- "Outside contributor, public repo" → fork-and-pull. "Internal team with push" → shared repository. Both still use pull requests.
- Files changed is the diff. Conversation is the discussion. Checks are CI. Commits are the list of commits. Do not mix them.
- Squash is not "delete Git history of the repository." It is one commit on the base branch for that PR.
- Rebase-and-merge on GitHub is not identical to local
git rebase(new SHAs, dropped empty commits). - You cannot merge a draft. Marking ready both enables merge (subject to protections) and requests CODEOWNERS.
- Merge requires write. Review requires read. Requesting a reviewer requires write (or triage, for assigning a reviewer on an org repo).
If you can walk a PR from draft to submitted review to a chosen merge method, and you can say whether the head branch lived in a fork or in the same repo, you have the Domain 3 pull request skill.
What is true of a draft pull request on GitHub?
An outside contributor has read access to a public repository but not write access. How do they propose a change with a pull request?
A team wants default-branch history to stay linear and wants each reviewed commit on a short-lived pull request preserved, not folded into one commit. Which merge method matches that goal?