Push, Pull, and Fetch

7 min read

Your local repo and the remote repo are two separate copies of the same project. They drift apart the moment either side commits. Three commands keep them in sync — git push sends your work up, git pull brings teammates' work down, and git fetch peeks at the remote without actually merging anything. This lesson nails down when to use each, the upstream concept that makes the short forms work, and the morning routine every QA engineer runs before they touch a test file.

The mental model

Think of your repo as having two layers:

  • Local — what's on your laptop. Your commits land here first.
  • Remote — what's on GitHub. Your team's commits land here.

Neither side automatically knows what the other did. Git only syncs when you ask. That's a feature, not a bug — it's why you can keep working offline.

git push — send your local commits up

Once your commits exist locally, push them to GitHub:

git push origin main
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 412 bytes | 412.00 KiB/s, done.
To github.com:acme/webapp-tests.git
   8f31320..4c48901  main -> main

Two arguments matter:

  • origin — the remote name. By default git clone calls the remote origin. Confirm with git remote -v.
  • main — the branch name. You're pushing your local main to the remote's main.

For a feature branch:

git push origin feature/search-tests

The first time you push a brand-new branch, add -u (short for --set-upstream):

git push -u origin feature/search-tests
Branch 'feature/search-tests' set up to track remote branch 'feature/search-tests' from 'origin'.

-u records "this local branch tracks that remote branch." From then on, plain git push and git pull work without arguments — Git remembers which remote and branch they refer to.

git pull — fetch and merge in one step

To bring down everyone else's commits:

git pull origin main
remote: Counting objects: 100% (8/8), done.
remote: Compressing objects: 100% (4/4), done.
Unpacking objects: 100% (5/5), done.
From github.com:acme/webapp-tests
 * branch            main       -> FETCH_HEAD
Updating 4c48901..9b3d2f1
Fast-forward
 tests/checkout.spec.js | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

git pull is two commands rolled into one: git fetch (download remote commits into your local copy of the remote refs) followed by git merge (merge those commits into your current branch). If the merge is clean, you don't notice; if there's a conflict, you handle it the way you learned in Chapter 2.

With upstream set, the short form works:

git pull

git fetch — peek without merging

Sometimes you want to know what's on the remote without changing your local files. git fetch downloads the remote's commits into your local Git database but does not merge them into your branch.

git fetch origin
remote: Enumerating objects: 6, done.
From github.com:acme/webapp-tests
   4c48901..9b3d2f1  main          -> origin/main
 * [new branch]      feature/api-tests -> origin/feature/api-tests

Two things are now visible in your local repo:

  • origin/main has moved forward. Your local main is unchanged — but you can see what's coming.
  • A new remote branch feature/api-tests exists. Your teammate just pushed it.

Inspect the new commits before deciding to merge:

git log main..origin/main --oneline
9b3d2f1 Add checkout regression tests
e1a4c80 Update timeout config for slower staging

If you're happy, run git merge origin/main to bring them in. If something looks risky, you can branch off, talk to the author, or wait. Fetch is the "look before you leap" command.

When to use which

CommandWhat it doesWhen to run
git pushSend your local commits upAfter you've committed and want teammates / CI to see your work
git pullDownload AND merge remote commitsAt the start of work, before branching off main
git fetchDownload remote commits but don't mergeWhen you want to inspect first, or check if anything new is up

The golden rule: pull before you push

If you push commits and the remote has commits you don't have locally, GitHub will reject the push:

git push origin main
To github.com:acme/webapp-tests.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'github.com:acme/webapp-tests.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally.

The fix: pull first, then push.

git pull
# resolve any merge if needed
git push

Build the habit of git pull at the start of every work session. It's cheap and prevents 90% of push-rejection moments.

The sync flow at a glance

Step 1 of 6

Start of day — git pull

Bring main up to date. Make sure your starting point matches what the team merged overnight.

A QA morning routine

Every workday for the rest of your career starts something like this:

cd ~/projects/webapp-tests
git switch main
git pull                                        # latest team commits
npx cypress run                                 # smoke test the suite
git switch -c test/add-discount-code-cases      # branch for today's work
# ...write tests, commit, repeat...
git pull origin main                            # before pushing, sync again
git push -u origin test/add-discount-code-cases
# ...open PR on GitHub...

Six commands, ten minutes, and you're already shipping work. The patterns become automatic within a week.

What git remote actually is

origin isn't magical — it's just a name pointing at a URL:

git remote -v
origin  git@github.com:acme/webapp-tests.git (fetch)
origin  git@github.com:acme/webapp-tests.git (push)

You can have multiple remotes. Common pattern: origin (your fork) and upstream (the original repo) — Lesson 4 of this chapter covers exactly that.

⚠️ Common mistakes

  • Pulling without committing your in-progress changes. If you have uncommitted edits and git pull triggers a merge, Git refuses with "Your local changes would be overwritten by merge." The fix isn't to discard the work — git stash (Chapter 4) parks it safely; pull; pop the stash back.
  • Using git pull blindly when your branch is messy. If you've been working on a branch for a week and run git pull without context, you might merge unrelated commits in. On a feature branch, git pull --rebase (or just review what git fetch reveals first) is often cleaner.
  • Forgetting -u on the first push. Without -u, future git push and git pull commands won't know which remote/branch to use, and you'll keep typing origin <branch-name>. Always push new branches with -u origin <branch> the first time.

🎯 Practice task

A full sync cycle. 20 minutes. Two terminal windows, or two folders, work best — pretend each is a different teammate.

  1. Clone any small repo of yours into two folders: webapp-tests-A and webapp-tests-B. (Or use one folder and a real teammate's clone.)
  2. In folder A, on main, edit a file, commit, and git push origin main.
  3. In folder B, run git status. Notice nothing changed locally. Then git fetch origin and git log main..origin/main --oneline. The new commit shows up — but folder B's working files are untouched.
  4. In folder B, run git pull. The commit now lands in your branch and the file updates.
  5. In folder B, edit a different file and commit. Try to git push before doing anything in folder A — confirm it succeeds. Then in folder A, git pull to bring it down.
  6. Stretch: in folder A, edit the same file folder B just changed, commit, and try to push without pulling first. Confirm the rejection message. Resolve it: git pull, fix any conflict, git push.

The next lesson uses every concept so far in service of the most important Git ritual on a team: the pull request.

// tip to track lessons you complete and pick up where you left off across devices.