Pull Requests and Code Review

A pull request (PR) — called a merge request on GitLab — is a proposal to merge one branch into another, hosted on a site like GitHub. It shows the changes, runs automated checks, and gives teammates a place to review and discuss before anything reaches main. It's how most teams and open-source projects accept code.

The workflow at a glance

  1. Update main and create a branch for the change.
  2. Commit your work and push the branch.
  3. Open a pull request on GitHub from your branch into main.
  4. Automated checks run; teammates review and comment.
  5. Push more commits to the same branch to address feedback — the PR updates itself.
  6. Once approved, merge the PR on GitHub, then delete the branch.
  7. Locally, switch back to main and pull.

1–2. Branch, commit, push

$
git switch main
git pull
git switch -c feature/discount-codes
Output
Already on 'main'
Your branch is up to date with 'origin/main'.
Already up to date.
Switched to a new branch 'feature/discount-codes'
$
printf 'function total(items, discount = 0) {\n  const sum = items.reduce((s, i) => s + i.price, 0);\n  return sum * (1 - discount);\n}\n' > basket.js
git commit -am "Apply discount codes to basket total"
git push -u origin feature/discount-codes
Output
[feature/discount-codes 0568c86] Apply discount codes to basket total
 1 file changed, 3 insertions(+), 2 deletions(-)
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 10 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 357 bytes | 357.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/sam-rivera/shop.git
 * [new branch]      feature/discount-codes -> feature/discount-codes
branch 'feature/discount-codes' set up to track 'origin/feature/discount-codes'.

On GitHub, the push output also contains a remote: line with a link to create a pull request for the new branch, and the repository page shows a "Compare & pull request" button.

3. Opening the pull request

On the PR form, choose the base branch (where the changes should go, usually main) and the compare branch (yours). Then write:

  • A title that summarises the change, like a good commit summary.
  • A description: what changed and why, how to test it, screenshots for visual changes, and links to issues (Closes #57 closes the issue when the PR merges).

If the work isn't finished but you want early feedback, open it as a draft pull request. With the GitHub CLI you can do all of this from the terminal:

$
gh pr create --base main --title "Apply discount codes to basket total" --body "Closes #57"
gh pr view --web          # open it in the browser

4. Review

Reviewers read the Files changed tab, leave comments on specific lines, and finish with one of: Comment, Approve or Request changes. Meanwhile automated checks (tests, linting, builds — often GitHub Actions) report a green tick or red cross on the PR.

Good reviews are about the code, not the person: ask questions, explain the reason behind a suggestion, and separate must-fix issues from optional nitpicks. As an author, keep PRs small — a few hundred lines at most — so they can be reviewed properly.

5. Responding to feedback

A reviewer points out that a discount above 100% would make the total negative. Sam fixes it on the same branch and pushes; the new commit appears in the PR automatically:

$
printf 'function total(items, discount = 0) {\n  const sum = items.reduce((s, i) => s + i.price, 0);\n  const rate = Math.min(Math.max(discount, 0), 1);\n  return sum * (1 - rate);\n}\n' > basket.js
git commit -am "Clamp discount between 0 and 100%"
git push
Output
[feature/discount-codes 97b131d] Clamp discount between 0 and 100%
 1 file changed, 2 insertions(+), 1 deletion(-)
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 10 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 382 bytes | 382.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/sam-rivera/shop.git
   0568c86..97b131d  feature/discount-codes -> feature/discount-codes

If main has moved on and your PR now conflicts, update your branch: merge main into it, or rebase it onto main and push with --force-with-lease. GitHub's "Update branch" button does the merge version for you.

6. Merging the pull request

GitHub offers three merge buttons; repository settings decide which are allowed:

OptionWhat lands on mainGood for
Create a merge commitAll the branch's commits plus a merge commitKeeping the full detail of how the work evolved
Squash and mergeOne new commit containing all the changesA clean main with one commit per PR, hiding "fix typo" commits
Rebase and mergeEach commit replayed onto main, no merge commitLinear history when every commit is already tidy

After merging, GitHub offers a "Delete branch" button (or deletes it automatically if the repository is set up to).

7. Cleaning up locally

The PR was merged with a merge commit and the branch deleted on GitHub. Sam brings main up to date and removes the local branch:

$
git switch main
git pull --prune
Output
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
From https://github.com/sam-rivera/shop
 - [deleted]         (none)     -> origin/feature/discount-codes
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (1/1), 228 bytes | 228.00 KiB/s, done.
   471a8db..e2cc0e0  main       -> origin/main
Updating 471a8db..e2cc0e0
Fast-forward
 basket.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)
$
git branch -d feature/discount-codes
git log --oneline --graph
Output
Deleted branch feature/discount-codes (was 97b131d).
*   e2cc0e0 (HEAD -> main, origin/main, origin/HEAD) Merge pull request #58 from sam-rivera/feature/discount-codes
|\
| * 97b131d Clamp discount between 0 and 100%
| * 0568c86 Apply discount codes to basket total
|/
* 471a8db Add basket total

If the PR had been squash-merged, git branch -d would refuse with "not fully merged": the squash created a new commit, so Git can't tell your branch's commits are in main. Check the PR is merged, then delete with -D.

Protecting main

Repository admins can add branch protection rules (or rulesets) on GitHub so that main can only change through pull requests: requiring approvals, passing checks, an up-to-date branch, and blocking force pushes. A CODEOWNERS file can automatically request reviews from the people responsible for particular folders.

Try it yourself

In a repository you own on GitHub, make a branch, push a change and open a pull request. Add a comment on one line of your own diff, push a follow-up commit, then merge with "Squash and merge". Finally update your local main and delete the branch locally with -D.