If you work with Git in a team, you've probably run into this scenario more than once: a teammate creates a new branch, and you need to pull it down locally and start working on it. Once you've done it a few times, it feels routine — but if you don't have a solid understanding of how fetch, checkout, and tracking relate to each other, you can easily end up working on the wrong branch or running into errors when pushing or pulling. This article breaks down the full workflow in a clear, systematic way.
What Is a Remote Branch, Anyway?
Before diving into the steps, let's take a moment to clarify what a "remote branch" actually is. Git is a distributed version control system, which means each developer's local environment and the remote repository (GitHub, GitLab, etc.) exist as completely independent repositories.
A remote branch is simply a branch that exists on the remote repository. On your local machine, Git maintains something called a "remote-tracking branch" to represent the state of the remote — these appear as origin/main or origin/feature/user-auth.
This distinction matters more than it might seem. For example, origin/feature/user-auth is not the remote branch itself — it's a snapshot of what that branch looked like the last time you ran git fetch. In other words, if you haven't fetched recently, your remote-tracking branches are out of date.
# Check your remote repository configuration
git remote -vExample output:
origin git@github.com:your-org/your-repo.git (fetch)
origin git@github.com:your-org/your-repo.git (push)If you have multiple remotes configured, you might also see entries like upstream or fork. Most team projects stick to a single origin, but if you're contributing to open source, working with multiple remotes becomes common.
Understanding git fetch: How to Pull Down Remote Branch Info
The first step to working with a teammate's branch is git fetch. The key thing to understand here is that fetch only downloads information from the remote — it doesn't touch your working directory at all.
# Fetch all updates from the remote
git fetch origin
# Fetch a specific branch only
git fetch origin feature/user-authAfter running fetch, Git creates or updates a remote-tracking branch like origin/feature/user-auth locally. At this stage, you've only "learned about" the remote branch — it doesn't yet exist as a local working branch.
When I first started using Git, I didn't really understand the difference between git pull and git fetch, so I'd just pull everything without thinking. But pull actually runs fetch and merge (or rebase) in one shot. To avoid unintentional merges, I'd recommend building the habit of fetching first, reviewing what changed, and then deciding how to proceed.
fetch vs. pull: A Concrete Comparison
Let's look at a concrete scenario to make the difference tangible.
Say you're working on your local main branch, and in the meantime someone pushes new commits to the remote main.
# With fetch: only downloads remote info
git fetch origin
# Your local main is untouched
# Only origin/main gets updated
git log --oneline main..origin/main # See commits on remote that aren't local yetThe git log main..origin/main command is incredibly useful. After fetching, you can safely inspect what changed on the remote before integrating anything. To see the actual diff, use git diff main..origin/main.
# With pull: fetches and integrates in one step
git pull origin main
# fetch + merge (or rebase) runs all at once
# If there's a conflict, you need to resolve it immediatelyPull is convenient, but it gives you no opportunity to review what's coming in. For small teams or low-traffic branches, pull is usually fine. But on larger projects or important branches, the two-step approach — fetch, then inspect, then merge — is much safer.
Listing Remote Branches
To see what remote branches are available, these commands come in handy:
# List remote branches
git branch -r
# List both local and remote branches
git branch -aExample output:
origin/main
origin/develop
origin/feature/user-auth
origin/feature/payment-api
origin/fix/login-errorOn projects with many branches, piping through grep helps narrow things down:
# Show only feature branches
git branch -r | grep feature/
# Filter by a teammate's name (if their name appears in branch names)
git branch -r | grep tanakaIf you've fetched but still can't see a branch, try git fetch --prune. Stale references to deleted remote branches can clutter your list and make it hard to find what you're looking for.
# Remove local references to branches deleted on the remote
git fetch --prune origin
# Or configure this to happen automatically on every fetch
git config --global fetch.prune trueOnce you set fetch.prune true, you'll wonder how you lived without it. Branches come and go constantly in team development, and stale references pile up fast. I once accidentally checked out a branch that had long been deleted on the remote and wasted time working from outdated code — this setting prevents exactly that.
Creating and Switching to a Local Branch with checkout
Once you've fetched the remote branch info, the next step is creating a local branch and switching to it. You can use either git checkout or git switch.
# Create a local branch with the same name as the remote and switch to it
git checkout feature/user-authThis command has a handy automatic behavior built in. If there's no local branch named feature/user-auth but exactly one remote branch called origin/feature/user-auth exists, Git will automatically create a local branch based on that remote-tracking branch. This behavior is known as "DWIM mode" (Do What I Mean).
On success, you'll see output like this:
branch 'feature/user-auth' set up to track 'origin/feature/user-auth'.
Switched to a new branch 'feature/user-auth'When you see this message, both the local branch creation and tracking setup have been handled automatically.
That said, in environments with multiple remotes configured, DWIM mode may not resolve correctly. In those cases, be explicit:
# Explicitly specify the remote-tracking branch
git checkout -b feature/user-auth origin/feature/user-authYou can also use git switch, introduced in Git 2.23. The motivation was to split checkout's dual responsibilities — branch switching and file restoration — into separate, focused commands.
# The switch equivalent
git switch feature/user-auth
# Explicit version
git switch -c feature/user-auth origin/feature/user-authWhen to Use checkout vs. switch
Which command to use is ultimately a team decision, but I've noticed more new projects adopting switch. Neither is strictly better, so the most important thing is to pick one and stay consistent across the team.
Here's a quick breakdown of the difference:
# checkout does double duty: branch switching AND file restoration
git checkout feature/user-auth # Switch branch
git checkout -- index.html # Discard changes to a file (restore)
# switch + restore separates these responsibilities
git switch feature/user-auth # Switch branch
git restore index.html # Discard changes to a file (restore)When a filename and branch name collide, git checkout can behave unexpectedly. For instance, if a file named main exists in your repo, git checkout main becomes ambiguous. git switch and git restore eliminate this ambiguity entirely.
Common Errors and How to Fix Them
Here are the errors you're most likely to encounter when creating a local branch:
Error 1: A local branch with that name already exists
fatal: a branch named 'feature/user-auth' already exists
In this case, just switch to the existing branch:
git switch feature/user-auth
# If you want to pull in the latest from remote:
git pullError 2: You have uncommitted changes
error: Your local changes to the following files would be overwritten by checkout:
src/auth.ts
Please commit your changes or stash them before you switch branches.Use git stash to temporarily shelve your work:
# Stash current changes
git stash
# Switch to the target branch
git switch feature/user-auth
# When done, go back to your original branch and restore the stash
git switch -
git stash popgit stash is a lifesaver when you need to quickly peek at another branch. Just don't let stashes pile up — run git stash list occasionally so you don't lose track of what you've stashed.
Understanding and Configuring Branch Tracking
The link between a local branch and its corresponding remote branch is called "tracking" (or upstream tracking). When you create a local branch using checkout or switch as shown above, tracking is configured automatically. But if you create a branch manually, or if the tracking association gets broken, you'll need to set it explicitly.
# Set the upstream for an existing local branch
git branch --set-upstream-to=origin/feature/user-auth feature/user-auth
# Shorthand
git branch -u origin/feature/user-authTo verify that tracking is configured correctly:
# Show tracking info for all branches
git branch -vvExample output:
main a1b2c3d [origin/main] Latest commit message
* feature/user-auth e4f5g6h [origin/feature/user-auth] Implement authenticationIf you see [origin/feature/user-auth] next to the branch name, tracking is set up. With tracking in place, git pull and git push work without any extra arguments — a small thing that adds up to a significant quality-of-life improvement day to day.
I used to overlook tracking entirely when I was starting out. Once I understood it, I never had to type out the remote branch name on every push again, and my workflow became noticeably smoother.
The Error You Get Without Tracking
The importance of tracking becomes crystal clear the first time you hit this error:
git pushfatal: The current branch feature/user-auth has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin feature/user-authHelpfully, the error message tells you exactly how to fix it. Running the suggested command sets up tracking and pushes at the same time:
# Push and configure tracking in one step
git push -u origin feature/user-authOn my team, we have a rule: always use -u on the first push of a new branch. That one extra flag makes every subsequent push and pull frictionless.
Inspecting Tracking Details
If you need more detail than git branch -vv provides:
# Show tracking status with ahead/behind info
git status -sbExample output:
## feature/user-auth...origin/feature/user-auth [ahead 2, behind 1]
M src/auth.tsThis tells you your local branch is 2 commits ahead of the remote (ahead 2) and the remote has 1 commit your local branch doesn't have yet (behind 1). This is useful context to have before pushing or pulling.
A Practical Workflow You Can Use Every Day
Bringing everything together, here's the full workflow for pulling down a remote branch and getting to work:
# 1. Fetch the latest from remote and clean up stale references
git fetch --prune origin
# 2. Confirm the target branch exists
git branch -r | grep feature/user-auth
# 3. Create and switch to the local branch
git switch feature/user-auth
# 4. Verify tracking is configured correctly
git branch -vv
# 5. Do your work, commit, and push
git add .
git commit -m "Fix authentication logic"
git pushOnce this flow becomes second nature, you'll rarely get tripped up by remote branch operations in team development.
One important caveat: if you're resuming work on a branch that hasn't been touched in a while, always run git pull before continuing to bring in any remote changes. If conflicts arise, it's safer to fetch first, understand the state of things, and then resolve.
Workflow for Resuming Work on an Abandoned Branch
Resuming work on a long-dormant branch is a common scenario. Here's how to approach it cleanly:
# 1. Check your current branch and status
git status
git branch -vv
# 2. Fetch the latest from remote
git fetch --prune origin
# 3. Switch to the target branch
git switch feature/user-auth
# 4. Pull in the latest remote changes
git pull
# 5. Incorporate the latest from main (catches conflicts early)
git merge origin/mainStep 5 is easy to skip, but it's important. A branch that's been idle for a while can drift significantly from main. It's far less painful to surface and resolve conflicts before you start new work than to discover them after.
If conflicts do come up, stay calm and work through them methodically:
# See which files have conflicts
git status
# After resolving conflicts in your editor:
git add <resolved-file>
git commit -m "Resolve merge conflicts with main"Tips for Switching Between Branches Efficiently
In team development, you'll often jump between branches — checking a teammate's branch for review, then heading back to your own work. These tricks help:
# Switch back to the previous branch (like cd - in the shell)
git switch -
# Review recent branch-switch history
git reflog | grep "checkout:"git switch - is surprisingly handy. The entire "check a review branch, then get back to my own branch" cycle becomes a single command.
Troubleshooting: Common Problems and Solutions
Even with a solid understanding of the workflow, you'll still run into issues in real-world team development. Here are problems I've actually encountered, along with their fixes.
"Invalid reference" — Branch Not Found
You're sure you fetched, but Git says the branch doesn't exist.
git switch feature/payment
# error: invalid reference: feature/paymentFirst, double-check the branch name. Typos are a surprisingly common culprit.
# Search by partial name
git branch -r | grep payment
# Maybe it's actually this:
# origin/feature/payment-apiThe -api suffix strikes again. Also worth checking: did your teammate actually push the branch yet? If not, no amount of fetching will help — just ask them.
Deleted Remote Branch Still Showing Locally
When a branch gets merged and deleted on the remote, your local remote-tracking reference doesn't disappear automatically.
# Remove stale references
git fetch --prune origin
# List local branches that have been merged into main
git branch --merged main
# Delete them after confirming
git branch -d feature/old-featuregit branch --merged main lists branches that are safe to delete. Just scan the list before running the delete to make sure nothing unexpected is in there.
Accidentally Ended Up in Detached HEAD State
Checking out a remote-tracking branch directly puts you in "detached HEAD" state:
# Don't do this — it creates a detached HEAD
git checkout origin/feature/user-authYou are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.Any commits you make in this state don't belong to any branch. If this happens, here's how to recover:
# Create a local branch to escape detached HEAD
git switch -c feature/user-auth
# Works even if you've already made commits in detached HEAD
git switch -c feature/user-authI fell into this trap early on by accidentally including origin/ in my checkout command. My commits seemed to vanish, which was alarming — but everything was recoverable via git reflog. Git is designed to be resilient; almost nothing is truly lost. Stay calm and investigate.
Git Config Tips for a Smoother Daily Experience
A few configuration settings I actually use that make these operations more pleasant:
# Automatically prune stale remote refs on every fetch
git config --global fetch.prune true
# Push to a remote branch with the same name as the current local branch
git config --global push.default current
# Use rebase instead of merge when pulling (keeps history linear)
git config --global pull.rebase true
# Sort branch list by most recently used
git config --global branch.sort -committerdatepush.default current is especially useful. With this set, you can push a new branch with just git push -u origin instead of typing the full git push -u origin feature/user-auth.
And branch.sort -committerdate sorts git branch output by most recently used. On projects with dozens of branches, this alone makes it much easier to find what you're looking for.
Conclusion: Mastering the Basics Elevates the Whole Team
Fetch, checkout, and tracking are the fundamentals of Git branch management. Looking back, I realize I used them for a long time without truly understanding them — and that vague understanding was the root cause of a lot of small frustrations.
When you understand each piece clearly: fetch safely downloads remote state, checkout/switch creates and switches local branches, and tracking wires local and remote branches together so push/pull just works — things click into place.
Start by building the core three-step loop into your daily habit: git fetch --prune origin → git switch <branch> → git branch -vv to confirm. Once that rhythm feels natural, remote branch operations will stop tripping you up.
When something goes wrong, remember that Git keeps a detailed history of everything in git reflog. Take a breath, check your state with git status and git branch -vv, and work through it step by step. Most problems have a clean solution.
Building this shared understanding across your team is what turns Git from a source of friction into a genuinely smooth foundation for collaborative development.
At aduce, we offer IT advisory services to help teams optimize their development processes — including Git workflows and team collaboration practices. If you're looking to improve your development workflow or level up your team's technical capabilities, feel free to reach out via our contact page.