Pull requests¶
In Intermediate Git, Sam merged branches alone, with git merge. Now that Alex contributes, Sam wants to see each change before it reaches main, and to discuss it if needed. A pull request does exactly that.
What is a pull request?¶
A pull request (PR) is a request to merge one branch into another, with a page to discuss and review the change before the merge happens. It:
- notifies the people concerned that changes are ready;
- lets the repository's owner and reviewers check the changes before they are added: the commits, the differences, the results of automated checks;
- keeps the discussion next to the code, in the history of the project;
- ends, when successful, with the two branches being merged.
This is why the good practice is to never commit directly to main: work on another branch, and propose it with a pull request. It is the heart of the GitHub flow:
%%{init: {"gitGraph": {"showBranches": true, "rotateCommitLabel": false}, "themeVariables": {"git0": "#43a047", "git1": "#1e88e5", "gitBranchLabel0": "#ffffff", "gitBranchLabel1": "#ffffff", "gitInv0": "#ffffff", "commitLabelFontSize": "13px"}}}%%
gitGraph TB:
commit id: "Create inventory"
branch add-printer
commit id: "Add network printer"
commit id: "Document printer driver"
checkout main
merge add-printer id: "Merge pull request #3"
- Create a branch from
main. - Commit on it.
- Open a pull request.
- Discuss and review; add commits if needed.
- Merge the pull request into
main, and delete the branch.
Base and compare¶
A pull request always involves two branches:
| Term | Meaning | Example |
|---|---|---|
| base | The branch that receives the changes | main |
| compare (or head) | The branch that contains the changes | add-printer |
GitHub shows them as base: main ← compare: add-printer: the arrow shows the direction of the merge. The pull request contains the commits of compare that base does not have, like git log main..add-printer, and the differences they introduce, like git diff main...add-printer.
For a pull request from a fork, each side also has a repository: base repository sam-rivera/homelab, base main ← head repository alex-martin/homelab, compare add-printer. The pull request is created in the base repository, where Sam can review and merge it.
Opening a pull request¶
There are several ways to reach the pull request form:
| Starting point | How |
|---|---|
| A branch you just pushed | The yellow banner add-printer had recent pushes... > Compare & pull request |
| The terminal | The link GitHub prints after git push: https://github.com/sam-rivera/homelab/pull/new/add-printer |
| The Pull requests tab | New pull request, then choose base and compare |
| A fork | Contribute > Open pull request, or compare across forks on the comparison page |
| The web editor | Create a new branch for this commit and start a pull request > Propose changes |
The comparison page first tells whether the merge will be simple:
| Message | Meaning |
|---|---|
| Able to merge. These branches can be automatically merged. | No conflict |
| Can't automatically merge. | A conflict: the pull request can still be created, but the conflict must be resolved before the merge |
| There isn't anything to compare. | compare has no commit that base does not have |
Below, the commits and the changed files are listed. Click Create pull request to open the form.
The pull request form¶
| Field | Purpose |
|---|---|
| Title | What the change does: Add network printer to the inventory. GitHub proposes the message of the commit, or the branch name |
| Description | Why, what, and how to test, in Markdown. A closing keyword such as Closes #2 links the issue and closes it at the merge |
| Reviewers | Who must review the change. They are notified, and the pull request waits for their review |
| Assignees | Who is responsible for moving the pull request forward, usually its author |
| Labels, Projects, Milestone | As for issues |
| Allow edits by maintainers | For pull requests from a fork: lets the maintainers of the base repository push commits to your branch (to fix a typo, for example) |
The button Create pull request opens it. Its arrow offers Create draft pull request: a draft cannot be merged, and does not request reviews yet; click Ready for review when it is finished.
Alex opens the pull request:
## Add the network printer
Adds `printer01` to the inventory and documents its driver.
Closes #2
### How to check
- `inventory.csv` has a `printer01` line
- `services.md` mentions CUPS
With Reviewers: sam-rivera, and Assignees: alex-martin.
Assignee vs reviewer
| Assignee | Reviewer |
|---|---|
| Is responsible for the pull request: answers comments, pushes fixes, makes sure it gets merged | Examines the changes before the merge, comments, and approves them or requests changes |
| Often the author | Someone other than the author: you cannot approve your own pull request |
The pull request page¶
Like an issue, a pull request has a number (#3), a title, and a state (Open, Draft, Merged, or Closed), with four tabs:
| Tab | Content |
|---|---|
| Conversation | The description, comments, review summaries, events, and the merge box at the bottom |
| Commits | The commits of the pull request |
| Checks | The results of automated checks (GitHub Actions...) |
| Files changed | The differences, file by file, where the review happens |
A pull request is live: it follows its branch. Every commit pushed to add-printer afterwards appears in it automatically, and the reviewers are notified. There is no need to open a new pull request to fix something: commit on the same branch, and push.
sequenceDiagram
participant A as Alex
participant G as GitHub
participant S as Sam
A->>G: git push -u origin add-printer
A->>G: Open pull request #3, reviewer: Sam
G-->>S: Review requested
S->>G: Comment: "Can you add the printer's IP to services.md?"
A->>G: git push (new commit on add-printer)
G-->>S: Pull request #3 updated
S->>G: Approve, then merge
Closing without merging¶
A pull request that will not be merged (the idea was abandoned, or a better one replaced it) can be closed with Close pull request, at the bottom of the Conversation tab. It stays readable, and can be reopened. Its branch is not deleted.
Summary¶
| Action | How |
|---|---|
| Open a pull request for a pushed branch | Compare & pull request, or the link printed by git push |
| Choose the branches | base (receives) ← compare (contains) |
| Propose a change from a fork | Contribute > Open pull request, or compare across forks |
| Open a work in progress | Create draft pull request, then Ready for review |
| Update a pull request | Push new commits to its branch |
| Abandon it | Close pull request |
Common mistakes¶
- Inverting base and compare.
baseis where the changes go; check the arrow. - Opening a new pull request for each fix. Push to the same branch: the pull request updates itself.
- Opening a pull request from a fork against the original by accident. The base repository defaults to the original: check it before clicking Create pull request.
- Mixing unrelated changes in one pull request. One subject per pull request makes it easy to review.
- No description. Explain what changes and why, and link the issue.
Hands-on labs¶
Three labs, from guided to more autonomous. They need your GitHub account; Lab 2 also needs Git and a token with Contents: Read and write on the practice repository (see Authenticating with personal access tokens). Replace <username> with your GitHub username. The pull requests are reviewed and merged on the next page: keep the repository until then.
Setup for Labs 1 and 2: on GitHub, create a public repository homelab-practice-10 with a README. Then create inventory.csv with the lines hostname,ip,role and pi-dns,192.168.1.10,dns, committed directly to main. Finally, create an issue Add the network printer (#1).
Lab 1: a pull request from the browser¶
Objective: propose a change from the web editor, fill in a pull request, and update it with a second commit.
Prerequisites and initial state: homelab-practice-10, with inventory.csv and issue #1.
Tasks:
- Edit
inventory.csvon GitHub to addprinter01,192.168.1.30,printer. In the commit dialog, choose Create a new branch for this commit and start a pull request, name the branchadd-printer, and click Propose changes. - On the comparison page, check the merge status, the base and compare branches, and the number of commits and files.
- Give the pull request the title
Add the network printer, a description that closes #1, assign yourself, and create it. - Look at each tab of the pull request.
- Still in the browser, switch to the branch
add-printer, createservices.mdwith- printer01: CUPS, and commit it directly toadd-printer. Go back to the pull request.
Expected result and verification:
- Task 2: Able to merge, base:
main← compare:add-printer, 1 commit, 1 file changed. - Task 3: the pull request is
#2(issue #1 took number 1), Open, and its Development section links issue #1. Issue #1 shows the link too. - Task 5: the pull request now has 2 commits and 2 files changed, without being recreated.
Solution
-
Description:
Adds the network printer to the inventory. Closes #1Assignees > assign yourself, then Create pull request.
-
Branch selector >
add-printer, then Add file > Create new fileservices.md, and Commit directly to theadd-printerbranch. -
The pull request follows the branch: any commit on
add-printeris part of it. - You cannot add yourself as a reviewer: reviewers are other people.
Keep homelab-practice-10 and the pull request for the next page.
Lab 2: from the terminal, as a draft¶
Objective: push a local branch, open its pull request from the link printed by Git, as a draft, and meet a pull request that cannot be merged automatically.
Prerequisites and initial state: homelab-practice-10 from the setup, a token with write access to its contents.
Setup:
mkdir -p ~/git-practice/pr && cd ~/git-practice/pr
git clone https://github.com/<username>/homelab-practice-10.git
cd homelab-practice-10
git config user.name "Sam Rivera"
git config user.email "[email protected]"
Tasks:
- Create a branch
fix-dns-ip, change the IP ofpi-dnsto192.168.1.11, commit with the messageFix pi-dns IP address, and push the branch with an upstream. Find the link in the output. - Open the link, and create the pull request as a draft. Check that it cannot be merged.
- On GitHub, on
main, change the IP ofpi-dnsto192.168.1.12and commit directly tomain(as if someone else did it meanwhile). - Look at the merge box of the draft pull request.
- Mark the pull request Ready for review.
Expected result and verification:
- Task 1: the push output contains
Create a pull request for 'fix-dns-ip' on GitHub by visiting:followed by the/pull/new/fix-dns-iplink. - Task 2: the pull request is labeled Draft; the merge box says it is still a work in progress.
- Task 4: the merge box reports This branch has conflicts that must be resolved, with the conflicting file
inventory.csv. - Task 5: the pull request is Open; the conflict remains (it is resolved on the next page, or by updating the branch).
Solution
cd ~/git-practice/pr/homelab-practice-10
git switch -c fix-dns-ip
sed -i 's/192.168.1.10/192.168.1.11/' inventory.csv
git commit -am "Fix pi-dns IP address"
git push -u origin fix-dns-ip
# remote: Create a pull request for 'fix-dns-ip' on GitHub by visiting:
# remote: https://github.com/<username>/homelab-practice-10/pull/new/fix-dns-ip
- Both branches changed the same line from the same starting point: exactly the situation of merge conflicts. GitHub detects it before anyone tries to merge.
- A conflict does not block the creation of a pull request, only its merge.
Keep the clone, the repository, and both pull requests for the next page.
Lab 3: a pull request in your own fork¶
Objective: open a pull request in a fork without sending it to the original repository, by choosing the base repository yourself.
Prerequisites and initial state: a GitHub account. A fork of octocat/Spoon-Knife in your account (create one with Fork if you deleted the one from Cloning and forking).
Tasks:
- In your fork, create a branch
my-changeand commit a small change toindex.htmlon it, from the browser. - Open a pull request for
my-change. Before creating it, look at which base repository GitHub selected. - Change the base repository so that the pull request stays in your fork, with base
main. Create it. - Check where the pull request appears: in your fork's Pull requests tab, or in
octocat/Spoon-Knife's?
Expected result and verification:
- Task 2: the base repository is
octocat/Spoon-Knife: by default, a pull request from a fork targets the original. - Task 3: after selecting
<username>/Spoon-Knifeas base repository, the form shows only base:main← compare:my-change, as in a normal repository. - Task 4: the pull request is listed in your fork, numbered
#1;octocat/Spoon-Knifeis unaffected.
Solution
- On the comparison page, the base repository dropdown lists the original and your fork. Choose your fork: the head repository selector disappears, since both branches are in the same repository.
- This is how you rehearse a pull request on a fork without disturbing the original project's maintainers.
Keep the fork and its pull request for the next page, or close the pull request and delete the fork.