Use git fetch to update remote refs

Learn how git fetch updates remote-tracking references without merging or changing working files. Understand why it's a safe way to check remote changes, see how to run it, interpret output, and manage stale branches. Includes hands-on walkthrough, comparing fetch with pull and clone, troubleshooting common issues like

Focus: use git fetch to update remote references

Sponsored

You're in the middle of a feature branch, deep in flow, when you remember: 'I should check what changed on main.' Your instinct is to run git pull — but that can silently merge remote changes into your work, trigger conflicts you didn't ask for, or even update your branch history before you're ready. That's the pain: you want to see what's new on the remote without touching your local branches or working files. The solution is git fetch — a read-only command that updates your remote-tracking references, so you can inspect incoming changes at your own pace and merge only when you choose.

The problem this lesson solves

Imagine your team pushes a hotfix to main. You're on feature/payments. If you run git pull origin main, Git will merge that hotfix into feature/payments right then and there. That might be fine — or it might create merge conflicts that interrupt your flow. Worse, if you accidentally pull a remote branch with unrelated changes, your working tree becomes a messy mixture of your work and theirs.

The real problem: you lack a safe, non-destructive way to see what's happening on the remote — new commits, new branches, new tags — without altering your local state.

git fetch solves this by performing only the first half of git pull. It reaches out to the remote, downloads the new objects (commits, blobs, trees), and updates the remote-tracking references like origin/main. It does not touch your working directory, staging area, or current branch. You get a complete picture of remote changes without any risk to your own work.

By the end of this lesson, you'll be able to:

  • Explain what remote-tracking references are and why git fetch updates them.
  • Run git fetch safely in various scenarios.
  • Read fetch output to understand what changed on the remote.
  • Compare git fetch with git pull and git clone to choose the right command.
  • Troubleshoot common fetch errors and stale-branch issues.

Core concept / mental model

Think of Git as a local snapshot of a shared universe, and the remote as the 'source of truth' that others contribute to. Your local repository stores references (pointers) to commits, branches, and tags. Some of these references are local (like main or feature/x), and some are remote-tracking (like origin/main or origin/feature/x).

Remote-tracking references (refs/remotes/<remote>/<branch>) act as your memory of what the remote looked like last time you interacted with it. They are read-only local copies — you can't check them out directly, and Git keeps them updated only when you run commands like fetch, pull, or clone.

Here's the mental model in a picture (or words):

  1. You start with a local repo and a remote called origin.
  2. When you run git fetch, Git contacts origin, downloads all new commits and objects, and moves origin/main (and other origin/* refs) to point at the latest remote commits.
  3. Your local main and your working directory stay exactly as they were.

The key insight: git fetch is a pure read-only operation from your working tree's perspective. It never merges, never rebases, never changes your local branches. It only updates your knowledge of the remote. This makes it the perfect 'check before you leap' command.

How it works step by step

Let's break down what happens when you run git fetch — the mechanics behind the magic.

What Git does internally

  1. Reads your remote configuration — Git looks in .git/config and .git/refs/remotes/ to find the remote name (usually origin) and its URL.
  2. Connects to the remote over the configured protocol (HTTPS or SSH).
  3. Exchanges object references — your Git asks the remote: 'What refs do you have and at which commits?'
  4. Downloads missing objects — Git pulls down any commits, blobs, and trees it doesn't already have, compressing and transferring efficiently.
  5. Updates remote-tracking referencesorigin/main moves to the tip of the remote's main. If the remote has new branches, they appear as origin/new-branch. If the remote deleted a branch, your local origin/deleted-branch is removed under --prune (or via git fetch --prune).
  6. Prints a summary — the output shows which refs were updated, how many commits were new, and which tags were updated (if any).

The anatomy of git fetch output

remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 123 bytes | 1.0 KiB/s, done.
From https://github.com/user/myproject
   1a2b3c4..5d6e7f8  main       -> origin/main

The From line shows the remote URL. The line 1a2b3c4..5d6e7f8 main -> origin/main tells you that the remote's main moved from commit 1a2b3c4 to 5d6e7f8, and your tracking ref origin/main was updated accordingly.

Hands-on walkthrough

Let’s get practical. In this exercise, we’ll set up a remote, simulate a colleague pushing changes, and see how git fetch updates our references without touching our local files.

Setup: create a bare remote and a local clone

Open a terminal and run:

# Create a bare repository to act as a remote
mkdir /tmp/example-remote
cd /tmp/example-remote
git init --bare

# Create a local working repo (simulate a developer)
cd /tmp
mkdir example-local
git init
echo "Initial commit" > file.txt
git add . && git commit -m "Initial commit"

# Add the remote and push
cd /tmp/example-local
git remote add origin /tmp/example-remote
git push -u origin main

Now your local repo has a remote origin and a tracking ref origin/main pointing to the same commit as your local main.

Simulate a new push from a teammate

Pretend you're a teammate working elsewhere. Let's create another clone, make a commit, and push to the remote:

# In a separate directory, clone the remote
cd /tmp
git clone /tmp/example-remote /tmp/example-teammate
cd /tmp/example-teammate
lock-file-and-edit

Actually, let's keep it simple — just add a file and commit:

cd /tmp/example-teammate
echo "Teammate's change" > new-file.txt
git add . && git commit -m "Add teammate file"
git push origin main

Now the remote's main is ahead of our local main.

Run git fetch

Back in your original local repo (/tmp/example-local):

cd /tmp/example-local
git fetch origin

Expected output

remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From /tmp/example-remote
   a1b2c3d..e4f5a6b  main       -> origin/main

Now inspect your refs — notice that origin/main moved, but main hasn't:

# Show the refs and their commits
git log --oneline --graph --all

Output

* e4f5a6b (origin/main) Add teammate file
* a1b2c3d (HEAD -> main) Initial commit

Your local main is unchanged, but origin/main now points to the new commit. Your working directory still contains only file.txt — you’re perfectly safe.

Fetching specific branches and pruning

You can fetch just one branch to reduce network traffic:

git fetch origin main

Or, to clean up deleted remote branches from your local tracking refs:

git fetch --prune origin

Now if a teammate deletes a remote branch, your local origin/old-branch disappears after --prune.

Compare options / when to choose what

Now that you've seen git fetch in action, let's compare it with its cousins: git pull and git clone. Each has a different purpose.

Command What it does When to use it Risks
git fetch Downloads remote commits and updates remote-tracking refs. Your working tree is untouched. When you want to inspect changes before integrating them, or simply stay aware of remote state. None to your work. Safe to run anytime.
git pull Runs git fetch followed by a merge (or rebase). Updates your current branch with remote changes. When you're ready to incorporate remote changes into your local branch. Can create merge conflicts, alter your branch history, and disturb your flow.
git clone Creates a new local repo from a remote, with all refs and a working tree. Only once, when you don't have a local repo yet. Overwrites an existing directory (use carefully).

Pro tip: Make a habit of running git fetch first, then git log origin/main to review what's coming. Only when you're confident, run git merge --ff-only origin/main to fast-forward your local branch to match remote — this avoids surprise merge commits.

Troubleshooting & edge cases

Let's tackle common problems you'll hit when using git fetch.

1. git fetch says 'Could not resolve host' or permission denied

Error:

fatal: unable to access 'https://github.com/user/repo.git/': Could not resolve host: github.com

Why: Network issue, proxy, or wrong URL.

Fix: - Verify the remote URL with git remote -v. - If you're behind a proxy, configure it: git config --global http.proxy http://proxy:port. - For SSH, check your keys: ssh -T git@github.com.

2. git fetch doesn't delete stale branches automatically

Symptom: You deleted a branch on the remote, but git branch -r still shows origin/deleted-branch.

Why: git fetch by default won't remove local remote-tracking refs for branches that no longer exist on the remote (unless your git config sets --prune by default).

Fix: Run git fetch --prune or set the default:

git config --global fetch.prune true

3. git fetch seems to do nothing — no new updates

Symptom: Output shows "Everything up-to-date."

Why: The remote hasn't changed, or you already fetched the latest changes.

Fix: Not a problem — that's expected. If you believe changes exist, force a refetch with git fetch origin --tags or check for new tags.

4. You accidentally fetched a large repo — slow network

Fix: Fetch only the specific branch you need: git fetch origin main. Or use a shallow fetch: git fetch --depth=1 origin to get only the latest commit history.

What you learned & what's next

You've now mastered the core of git fetch: you can update your remote-tracking references without touching your local work, inspect incoming changes safely, and decide when to integrate them. That's the foundation for clean collaboration workflows.

Specifically, you learned:

  • What git fetch does — updates origin/* refs, downloads objects, never merges.
  • How to run itgit fetch, git fetch origin main, git fetch --prune.
  • How to interpret outputold..new branch -> origin/branch lines.
  • When to choose fetch vs pull vs clone — fetch for inspection, pull for integration, clone for setup.
  • How to troubleshoot — network issues, stale branches, and slow fetches.

This skill is your bridge to the next lesson: merging remote changes with git merge --ff-only. There you'll take the updated origin/main you just fetched and fast-forward your local branch safely. You're now ready to handle real-world collaboration with confidence.

Practice recap

Open your terminal and run git fetch on a repository you use regularly. Then run git log origin/main (or the default branch) to see what has changed on the remote. If you see new commits, try git merge --ff-only origin/main to fast-forward your local branch — you'll see how fetch and merge work together.

Common mistakes

  • Assuming git fetch updates your local branch — it only updates remote-tracking references like origin/main. Your local main stays where it is until you merge or rebase.
  • Forgetting --prune — deleted remote branches leave stale origin/* refs, cluttering your git branch -r output. Set fetch.prune=true globally to avoid this.
  • Using git pull when you only intended to inspect — this merges remote changes immediately, possibly creating conflicts that interrupt your work. Fetch first, then decide.
  • Not fetching specific branches when network is slow — fetching all refs of a huge repo can waste time. Use git fetch origin main to limit scope.

Variations

  1. Use git fetch --all to fetch from all remotes, not just origin.
  2. Use git fetch --tags to retrieve updated tags in addition to branches — useful for releases.
  3. Use git fetch --prune (or set fetch.prune=true) to automatically clean up stale remote-tracking references.

Real-world use cases

  • Before merging a teammate's long-running feature branch, fetch and inspect origin/feature-x to review all new commits without disturbing your current branch.
  • In a CI/CD pipeline, fetch a specific commit (e.g., git fetch origin <sha>) to build or test that exact point in history without configuring a working tree.
  • When doing support work, fetch updated tags to verify release artifacts — e.g., git fetch origin --tags to see if a patch was tagged after your last poll.

Key takeaways

  • git fetch updates remote-tracking references (origin/*) and downloads new objects without changing your working directory or current branch.
  • Your local branches remain untouched after git fetch — you must merge or rebase to integrate new remote commits.
  • Use git fetch --prune to keep remote-tracking refs in sync when branches are deleted remotely.
  • Always run git fetch before git log origin/main to ensure you're inspecting the latest remote state.
  • Prefer git fetch over git pull when you want to review changes before integrating them.
  • Fetching a specific branch (git fetch origin main) is faster and more focused than fetching all refs.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.