Aller au contenu

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"
  1. Create a branch from main.
  2. Commit on it.
  3. Open a pull request.
  4. Discuss and review; add commits if needed.
  5. 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. base is 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:

  1. Edit inventory.csv on GitHub to add printer01,192.168.1.30,printer. In the commit dialog, choose Create a new branch for this commit and start a pull request, name the branch add-printer, and click Propose changes.
  2. On the comparison page, check the merge status, the base and compare branches, and the number of commits and files.
  3. Give the pull request the title Add the network printer, a description that closes #1, assign yourself, and create it.
  4. Look at each tab of the pull request.
  5. Still in the browser, switch to the branch add-printer, create services.md with - printer01: CUPS, and commit it directly to add-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
  1. Description:

    Adds the network printer to the inventory.
    
    Closes #1
    

    Assignees > assign yourself, then Create pull request.

  2. Branch selector > add-printer, then Add file > Create new file services.md, and Commit directly to the add-printer branch.

  3. The pull request follows the branch: any commit on add-printer is part of it.

  4. 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:

  1. Create a branch fix-dns-ip, change the IP of pi-dns to 192.168.1.11, commit with the message Fix pi-dns IP address, and push the branch with an upstream. Find the link in the output.
  2. Open the link, and create the pull request as a draft. Check that it cannot be merged.
  3. On GitHub, on main, change the IP of pi-dns to 192.168.1.12 and commit directly to main (as if someone else did it meanwhile).
  4. Look at the merge box of the draft pull request.
  5. 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-ip link.
  • 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:

  1. In your fork, create a branch my-change and commit a small change to index.html on it, from the browser.
  2. Open a pull request for my-change. Before creating it, look at which base repository GitHub selected.
  3. Change the base repository so that the pull request stays in your fork, with base main. Create it.
  4. 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-Knife as 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-Knife is 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.