Curious Pandas

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.

15 min+35 XP for finishing

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.

Visual guideTwo pushes, one branch
Two pushes, one branchYour teammate pushes commit X to origin first. Your push of commit Y is rejected because origin has work you do not. You fetch, which updates origin/main in your repository, merge it with your work, and push the merge, which origin accepts.your mainbase, then Yoriginbase, then Xteammatepushed X firstorigin/mainyour copy of theirsmerge commitX and Y togetherpushrejectedfetchmergepushTwo pushes, one branchYour teammate pushes commit X to origin first. Your push of commit Y is rejected because origin has work you do not. You fetch, which updates origin/main in your repository, merge it with your work, and push the merge, which origin accepts.your mainbase, then Yoriginbase, then Xteammatepushed X firstorigin/mainyour copy of theirsmerge commitX and Y togetherpushrejectedfetchmergepush
  1. You and a teammate both start from the same base commit and each make one more.
  2. They push first. Origin now has X, which you have never seen.
  3. Your push is rejected. Accepting it would throw X away, so git refuses.
  4. Fetch downloads X into origin/main without touching your branch. Then merge it with your Y.
  5. 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.

Visual guideThe only door into main
The only door into mainWork happens on a feature branch. Opening a pull request proposes merging it into main. On a protected branch, the merge is allowed only when required status checks have passed and a reviewer has approved.feature branchyour commitspull requestproposed mergemainprotectedstatus checksthe pipelinereviewapprove or notopenmust passapprovemergeThe only door into mainWork happens on a feature branch. Opening a pull request proposes merging it into main. On a protected branch, the merge is allowed only when required status checks have passed and a reviewer has approved.feature branchyour commitspull requestproposed mergemainprotectedstatus checksthe pipelinereviewapprove or notopenmust passapprovemerge
  1. You work on a branch and push it. Nothing reaches main yet.
  2. A pull request proposes the merge and shows everyone the diff.
  3. Required checks run your pipeline on the proposed change. Red blocks the merge.
  4. 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.

Check yourself+10 XP

You run git fetch. Which of these changes?

Check yourself+10 XP

Your push is rejected with '(fetch first)'. What is the safe fix?

Check yourself+10 XP

main is protected: reviews and status checks are required. A teammate's pull request is approved, but one check is red. Can it merge?

Do it for real · your machine+50 XP bonus

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 --graph

Then 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.

Self-reported. We can't see your machine, and we don't want to. This is a bonus, never required.

Sources

Finished reading?

Missions and quizzes above earn their own XP. This marks the lesson done.