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
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 fetchupdates them. - Run
git fetchsafely in various scenarios. - Read fetch output to understand what changed on the remote.
- Compare
git fetchwithgit pullandgit cloneto 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 likefetch,pull, orclone.
Here's the mental model in a picture (or words):
- You start with a local repo and a remote called
origin. - When you run
git fetch, Git contactsorigin, downloads all new commits and objects, and movesorigin/main(and otherorigin/*refs) to point at the latest remote commits. - Your local
mainand 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
- Reads your remote configuration — Git looks in
.git/configand.git/refs/remotes/to find the remote name (usuallyorigin) and its URL. - Connects to the remote over the configured protocol (HTTPS or SSH).
- Exchanges object references — your Git asks the remote: 'What refs do you have and at which commits?'
- Downloads missing objects — Git pulls down any commits, blobs, and trees it doesn't already have, compressing and transferring efficiently.
- Updates remote-tracking references —
origin/mainmoves to the tip of the remote'smain. If the remote has new branches, they appear asorigin/new-branch. If the remote deleted a branch, your localorigin/deleted-branchis removed under--prune(or viagit fetch --prune). - 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 fetchfirst, thengit log origin/mainto review what's coming. Only when you're confident, rungit merge --ff-only origin/mainto 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 fetchdoes — updatesorigin/*refs, downloads objects, never merges. - How to run it —
git fetch,git fetch origin main,git fetch --prune. - How to interpret output —
old..new branch -> origin/branchlines. - 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 fetchupdates your local branch — it only updates remote-tracking references likeorigin/main. Your localmainstays where it is until you merge or rebase. - Forgetting
--prune— deleted remote branches leave staleorigin/*refs, cluttering yourgit branch -routput. Setfetch.prune=trueglobally to avoid this. - Using
git pullwhen 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 mainto limit scope.
Variations
- Use
git fetch --allto fetch from all remotes, not justorigin. - Use
git fetch --tagsto retrieve updated tags in addition to branches — useful for releases. - Use
git fetch --prune(or setfetch.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-xto 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 --tagsto see if a patch was tagged after your last poll.
Key takeaways
git fetchupdates 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 --pruneto keep remote-tracking refs in sync when branches are deleted remotely. - Always run
git fetchbeforegit log origin/mainto ensure you're inspecting the latest remote state. - Prefer
git fetchovergit pullwhen 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.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.