Remotes, pull requests and review
Your repository is one copy of many. Learn what fetch, pull and push really move, why git refuses a push when someone got there first, and how a pull request turns "it works on my machine" into a reviewed change.
So far every commit has lived in one repository: yours. Real projects have many copies of the same history - one on each laptop, and usually one on a server everybody agrees to share, like GitHub. Git calls those other copies remotes. When you clone, git names the one you cloned from origin.
Nothing syncs by itself. Three commands move commits between copies:
| Command | What it moves | What it changes |
|---|---|---|
git fetch |
their new commits, into your repository | only origin/main, your picture of their branch |
git pull |
the same, then merges | your current branch too |
git push |
your new commits, to them | their branch - if it’s safe |
Pro Git is careful about fetch: it only downloads the data, and doesn’t merge it with your work or change what you’re working on. That makes fetch always safe to run. pull is fetch plus a merge - the same merge you met in Conflicts are honesty.
When someone pushed first
Pro Git describes the everyday collision: if you and someone else clone at the same time, and they push and then you push, your push will rightly be rejected. You have to fetch their work and fold it into yours before you’re allowed to push.
- You and a teammate both start from the same base commit and each make one more.
- They push first. Origin now has X, which you have never seen.
- Your push is rejected. Accepting it would throw X away, so git refuses.
- Fetch downloads X into origin/main without touching your branch. Then merge it with your Y.
- Your branch now contains their work, so the push is a plain step forward and origin accepts it.
Here is the heart of what git 2.54 printed when the second push lost the race against a real remote:
! [rejected] main -> main (fetch first)
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
After a git fetch but before merging, the same push is refused as (non-fast-forward), with the hint that the tip of your current branch is behind. After merging origin/main, it goes through.
Pull requests: changes that get read
Pushing straight to main means every change reaches everyone unreviewed. A pull request pushes your work to a branch instead and asks for it to be merged. GitHub’s docs describe the review as a chance for people to comment on changes, suggest improvements, and approve or request changes before the code is merged.
Teams make that the only way in with protected branches. GitHub’s settings include requiring pull request reviews and requiring status checks to pass before merging. The status checks are your pipeline: the same checks, on every proposed change, before it can land.
- You work on a branch and push it. Nothing reaches main yet.
- A pull request proposes the merge and shows everyone the diff.
- Required checks run your pipeline on the proposed change. Red blocks the merge.
- A reviewer approves, comments, or requests changes. With both satisfied, the merge goes in.
A good review is mostly questions. Not “this is wrong”, but “what happens if this list is empty?” The author knows the change best. The reviewer’s job is to be the first person who didn’t write it.
You run git fetch. Which of these changes?
Fetch only downloads. It updates the remote-tracking branch, origin/main, and never touches your working branch. Pull is fetch plus a merge.
Your push is rejected with '(fetch first)'. What is the safe fix?
Someone pushed commits you don't have. Bring them in, resolve any conflicts, and your push becomes a plain step forward. --force would erase their commits from the remote.
main is protected: reviews and status checks are required. A teammate's pull request is approved, but one check is red. Can it merge?
Protection rules are all-of. An approval says a person read it; a passing check says the pipeline did. Neither stands in for the other.
Everything above ran in your browser. Now try it in a real terminal: any Linux machine, a Mac's Terminal app, or Windows with WSL. Nothing to install beyond that.
No account needed: a bare repository on your own disk is a perfectly good remote.
git init --bare remote.git
git clone remote.git alice && git clone remote.git bob
cd alice && git commit --allow-empty -m "base" && git push origin HEAD:main && cd ..
cd bob && git fetch && git switch main && cd ..
cd alice && git commit --allow-empty -m "X" && git push origin HEAD:main && cd ..
cd bob && git commit --allow-empty -m "Y" && git push origin main # rejected (fetch first)
git pull --no-rebase --no-edit && git push origin main # merged, accepted
git log --oneline --graphThen open a real pull request in CuriousPandas/practice, a repository that exists for exactly this: fork it, add visitors/<your-username>.md (any letter case works) on a branch, push, and open a pull request against its main. Its visitor-file check is your status check - if it goes red, read why, fix it on the same branch and push again. Its main is protected like the quiz above: one approval and a green check. Practice pull requests are closed after 14 days; that is not a rejection.
Sources
Missions and quizzes above earn their own XP. This marks the lesson done.