The fast answer for git checkout remote branch: run git fetch,
then git checkout -b feature origin/feature (or just git checkout
feature if the name is unambiguous), then confirm with git branch -vv. That
three-step path — fetch, checkout, verify — covers the vast majority of real-world checkouts.
This guide covers what checkout actually does under the hood, the exact commands for checking
out a new branch from a remote in every situation (single remote, multiple remotes, forks), when to
reach for git switch instead, and the parts most tutorials skip entirely:
authentication failures during checkout, checking out branches inside CI/CD without ending up
stuck in detached HEAD, and git worktree as an alternative to checkout when you need
two branches open at once. If you haven't cloned
the remote git repository yet, start there — everything below assumes a local clone with at
least one remote already configured.
What Git Checkout Actually Does
What is git checkout? It's the command that changes what your working directory and
HEAD point to — swap in a different branch, a specific commit, a tag, or even a
single file's contents from somewhere else. Three targets, one command, which is exactly why
"what does git checkout do" has more than one right answer depending on what you hand it.
# Switch to an existing local branch
git checkout main
# Create a new branch and switch to it in one step
git checkout -b feature-x
# Detach HEAD at a specific commit — no branch involved
git checkout a1b2c3d
# Restore one file's contents from another branch, leave HEAD alone
git checkout main -- src/api/retry.ts Read that as a compact set of examples, one line per target type: switch to a branch that already exists, create a new branch and land on it in a single step, detach at a bare commit, or pull one file back. Only the first two are branch operations at all — and only the first two are what most people mean when they ask how to check out a branch in git.
What does checkout mean in git, mechanically: it rewrites your working directory's files to
match the target, and it moves HEAD to point at that target — either a branch
(refs/heads/<name>) or, when there's no branch involved, directly at a commit.
git checkout main is a request to see what main looks like right now;
it doesn't create anything, copy anything to a server, or touch history. Uncommitted changes
that would be silently overwritten by the swap block the checkout instead of getting lost — one
of the errors covered in the troubleshooting section below. So what does git checkout do, in one
line: it points HEAD at a new target and makes the files on disk agree with it.
git checkout -b meaning, specifically: -b is "branch" — create a new
branch pointing at your current commit (or an explicit starting point you name after it), then
check it out. The general shape is git checkout -b <branchname> <start-point>,
and it collapses the older git branch, git checkout two-step — create,
then switch — into one command. git checkout -b hotfix creates hotfix
from wherever HEAD currently sits; git checkout -b hotfix origin/hotfix
creates it from the tip of a remote-tracking branch instead, which is the exact form that matters
for checking out a new branch from a remote — covered in full in the next two sections.
What a Remote Branch Really Is
"Remote branch" gets used loosely for two different things, and the mix-up is where most
remote-branch checkout confusion starts. A branch that lives on the server — GitHub, GitLab,
wherever origin points — is one thing. A remote-tracking branch, something like
origin/main in your own local clone, is a different thing: a local bookmark
recording where that server branch pointed the last time you fetched.
# See your configured remotes
git remote -v
# See the remote-tracking branches your clone currently knows about
git branch -r origin/main is not a branch you can commit onto — it's read-only from your side,
overwritten wholesale on the next fetch to match whatever the server reports.
Checking it out directly, bare, with no -b, puts you in detached HEAD rather than on
a branch, because there's no local branch object backing it. That distinction — remote-tracking
ref versus local branch — is the entire reason git checkout -b feature origin/feature
exists as a separate form: it takes the read-only bookmark and turns it into a real, committable
local branch. Like remote-tracking refs, these bookmarks go stale the moment someone renames a
remote, which is one reason to know how to change a
remote's URL cleanly instead of half-reconfiguring it.
Checkout a Remote Branch: The Three-Command Path
How to check out a remote branch, the reliable version, is three commands: refresh your view of the server, create the local tracking branch, confirm it's wired up correctly. That's the whole routine for checking out a remote branch locally — nothing else is required, and every common failure below traces back to skipping one of the three.
# 1. Refresh remote-tracking refs
git fetch origin
# 2. Create a local branch tracking the remote one, and switch to it
git checkout -b feature origin/feature
# 3. Confirm the tracking relationship
git branch -vv
Step 1 matters more than it looks: if the branch was pushed after your last fetch, your local
origin/feature ref doesn't exist yet, and step 2 fails with "pathspec did not
match" — git fetch is what makes the branch visible locally in the first place, and
it's a read-only, side-effect-free network call, safe to run at any time.
Most of the time, there's a shortcut for step 2. Git's DWIM behavior — "do what I mean," in place since Git 1.8.4 (August 2013) — auto-creates the tracking branch for you when the name is unambiguous:
# Shortcut form — DWIM auto-creates a tracking branch from origin/feature
git checkout feature git checkout feature works exactly like git checkout -b feature
origin/feature under two conditions: no local branch named feature already
exists, and exactly one remote carries a branch with that name. Both conditions hold for the
common single-remote clone, which is why so many people never learn the longer explicit form —
until a second remote enters the picture and DWIM stops guessing, covered in its own section
below.
There's also an explicit long form that names the tracking relationship instead of relying on
the shortcut, functionally identical to -b with a remote-tracking start point:
git checkout --track origin/feature --track is worth reaching for when you want it unmistakably clear in a script or a
teammate's terminal history that you're deliberately setting up tracking, not just checking out
a branch that happens to already have one. All three forms — DWIM, -b, and
--track — land in the identical end state: a local branch named feature
whose upstream is origin/feature. Generalize the middle one to
git checkout -b <branchname> origin/<branchname> and you have the whole
remote-branch checkout routine in a single reusable line.
git switch vs git checkout
git switch landed in
Git
2.23, released August 2019, specifically to split
branch-switching away from everything else git checkout does. git checkout
overloads one command across branches, commits, and files, which is exactly the kind of ambiguity
that produces the "pathspec did not match" and accidental-detached-HEAD mistakes covered later in
this guide. git switch narrows the surface: it only switches branches, so there's
less to get wrong.
# git switch equivalents of the checkout commands above
git switch feature # git switch to remote branch via DWIM, same rule as checkout
git switch -c feature origin/feature # explicit tracking branch (-c, not -b)
git switch --track origin/feature # same as checkout --track
git switch main # git switch to another branch you already have locally
The remote case carries over unchanged: git switch feature switches to a remote
branch whenever feature exists only on the server, because git switch
inherits the identical DWIM rule. When the name is ambiguous, or you just want it spelled out,
git switch -c feature origin/feature is the explicit form that never has to guess.
Switching to another branch that already exists locally needs no flag at all —
git switch <branchname> and you're there.
The flag renames from -b to -c ("create") on purpose — different enough
from checkout -b that muscle memory doesn't silently carry over a habit from the
older command. If you learned git on the git branch, git checkout
two-step, git switch -c is that same pair collapsed into one command.
git switch was marked experimental at launch and shed that label as
of Git 2.36 (April 2022), so on any git from the last several years it's a fully supported,
stable command, not a preview feature.
git switch doesn't replace git checkout outright — it replaces the
branch-switching half of it. Detaching HEAD at an arbitrary commit and restoring a single file's
contents from another branch both stay on git checkout (or move to its 2019
companion command, git restore, for the file-restore case) — git switch
has no equivalent for either. For plain "get me onto this branch" work, git switch is
the better default on any modern git; for anything involving commits or files directly,
git checkout is still the tool.
When DWIM Fails: Multiple Remotes, origin vs upstream
DWIM's "exactly one remote has this branch" condition breaks the moment a second remote enters
the picture — the classic case being a fork, where origin points at your fork and a
second remote, conventionally named upstream, points at the original project.
# Add the original project as a second remote, commonly named upstream
git remote add upstream git@github.com:original-owner/project.git
git remote -v
# origin git@github.com:you/project.git (fetch)
# origin git@github.com:you/project.git (push)
# upstream git@github.com:original-owner/project.git (fetch)
# upstream git@github.com:original-owner/project.git (push) origin and upstream are just names — nothing in git treats either word
specially — but the convention is strong enough that most fork-based workflows assume it:
origin is wherever you can push, upstream is the source of truth you
pull updates from. With both remotes carrying a branch called fix, plain
git checkout fix can't guess which one you mean and either errors out or checks out
whichever remote git resolves first — don't rely on the tiebreak. Name the remote explicitly:
git fetch upstream
git checkout -b fix upstream/fix
# or, staying on git switch:
git switch -c fix upstream/fix git checkout -b fix upstream/fix checks out a branch from the upstream remote by
name: the remote is spelled out, so DWIM never gets a vote. Nothing is special about the word
upstream itself — git checkout -b fix origin/fix is the identical
command aimed at the other remote.
The same explicit-remote pattern applies to any checkout involving more than one remote —
anytime there's more than one remote in play, name it, rather than trusting DWIM to pick the
right one. Writing out <remote>/<branch> turns a guess into a
deterministic checkout, which is what you want in a script, a CI job, or a contribution workflow
you repeat every week.
Checking Out a Specific Commit, Tag, or Single File
What does checkout mean in git once branches are out of the picture? The same thing
mechanically — move HEAD, sync the working directory — just aimed at a target that
isn't a branch.
A branch, a commit, a tag, a single file — one command, git checkout, covers all
four targets, and only the first involves a branch at all. The branch case is the one covered
above; here are the other three.
# A tag — puts you in detached HEAD, same as a bare commit
git checkout v2.4.1
# A commit by hash — also detached HEAD
git checkout a1b2c3d
# One file's contents from another branch, HEAD untouched
git checkout develop -- path/to/file.ts
Checking out a tag or a raw commit hash detaches HEAD — there's no branch to update
as you move, just a direct pointer at that one commit. That's expected, not an error: it's the
normal way to inspect history at a fixed point, build a specific release, or bisect a
regression. Committing while detached still works, but nothing points at those new commits once
you check out something else, and git eventually garbage-collects them — create a branch first
with git checkout -b rescue-branch if you want to keep the work, which overlaps with
the recovery steps in resetting a branch to a specific
commit.
The file-restore form — git checkout develop -- path/to/file.ts — is different in
kind: it's a git checkout from another branch that never puts you on that branch. The
-- tells git everything after it is a path, not a branch name, so this
overwrites only that one file with its version from develop, leaving HEAD
and every other file untouched. On Git 2.23+, git restore --source develop path/to/file.ts
does the identical job with a name that doesn't imply "switch branches" at all — one more piece
of the overload git switch and git restore were built to split apart.
Troubleshooting: The Six Errors You'll Actually Hit
Six failure modes cover nearly every remote-branch checkout problem in practice. Each one has a specific, mechanical cause — none of them need guesswork once you know what to check.
1. "error: pathspec '...' did not match any file(s) known to git." The branch
doesn't exist under that name in any local or remote-tracking ref git currently knows about —
almost always because it was created on the server after your last fetch. Run git
fetch and retry; if it still fails, double-check the exact spelling with git branch
-a or git ls-remote --heads origin.
git fetch origin
git checkout feature-x 2. "error: Your local changes to the following files would be overwritten by
checkout." Git refuses to silently discard uncommitted work that conflicts with the
target branch. Commit the changes, or shelve them temporarily with
git stash, then check out and — if you stashed —
git stash pop once you're on the new branch.
3. Authentication and SSH failures during fetch or checkout. "Permission denied
(publickey)" or "fatal: could not read Username" happen at the network step — fetch
or the implicit fetch inside some checkout workflows — not the checkout logic itself. Verify the
remote URL protocol matches your configured credentials: git remote -v shows
whether you're on an SSH URL (git@github.com:...) or HTTPS
(https://github.com/...). For SSH, confirm the agent has your key loaded with
ssh -T git@github.com; for HTTPS, confirm a credential helper is configured and the
token hasn't expired. This is entirely separate from whether the branch exists — a correctly
spelled branch name still fails here if the transport layer can't authenticate.
4. "fatal: A branch named '...' already exists." You ran -b or
-c against a name that's already taken locally, often from an earlier, now-stale
checkout of the same feature. Either check out the existing branch directly
(git checkout feature, no -b — that flag is only for names that don't
exist yet), or delete the stale local branch first —
see deleting a local branch — before recreating
it from the current remote tip.
5. Unexpected detached HEAD. Checking out a remote-tracking ref directly
(git checkout origin/feature, no -b), a tag, or a bare commit hash all
land you in detached HEAD by design, not by accident — but it's an easy state to end up in
without meaning to, especially by pasting a hash from a log. git status reports
"HEAD detached at ..." when this happens; get back onto a branch with plain git checkout
main, or preserve any new commits first with git checkout -b rescue-branch.
6. Stale remote-tracking refs. git branch -a or plain
checkout can't see a branch that's brand new on the server, or keeps offering one
that was deleted there weeks ago — both are the same root cause, a remote-tracking ref that's out
of sync with the server. git fetch --prune refreshes and cleans it in one call; see
listing branches and checking the
current one for the full mechanics of how remote-tracking refs go stale.
Checkout in CI/CD and Automation
Checking out a remote branch inside CI is the same question with different defaults. Most CI systems optimize for speed over having a full, branch-aware clone, so checking out a branch on a GitHub Actions runner has a different failure profile than the identical command in your terminal.
GitHub Actions' actions/checkout defaults to fetch-depth: 1 — a shallow
clone containing just the one commit being built — and checks that commit out directly, which
leaves the runner in detached HEAD by design. That's normal for a build-and-test job that never
needs to commit anything. It becomes a real problem the moment a later step tries to push, tag,
or otherwise act like it's on a branch and the job fails with detached-HEAD-shaped errors that
look confusing outside the CI context. Two fixes, depending on what the job actually needs:
# Full history instead of a shallow, single-commit clone
- uses: actions/checkout@v4
with:
fetch-depth: 0
# Or, from inside a job script: fetch and check out a specific branch by name
git fetch origin main:main
git checkout main git fetch origin main:main is the direct-checkout shortcut for automation: it fetches
main from origin and writes it straight into a local branch also named
main in a single command, no separate checkout -b step needed. It's
useful specifically in scripts where you already know the exact branch name and don't want the
extra round trip of fetching everything first.
The general rule for CI: if a job only builds and tests, shallow + detached HEAD is fine and
faster. If a job needs to push, tag, compute a merge base, or otherwise reason about branch
history, use fetch-depth: 0 (or a comparable full-history flag on other CI
platforms) and an explicit checkout of the actual branch.
git worktree: Checking Out Two Branches at Once
Regular checkout has a hard constraint most tutorials leave out: one branch checked out at a time
per clone. Need to look at a hotfix branch without losing the half-finished state of your current
feature work — untracked files, an in-progress rebase, a build sitting mid-compile — and
git stash plus checkout plus stash pop is real friction for
something you're about to do again next week.
# Add a second working directory, checking out an existing branch into it
git worktree add ../hotfix hotfix
# Or create a new branch as part of adding the worktree
git worktree add -b hotfix-2 ../hotfix-2 origin/main git worktree add ../hotfix hotfix creates a second, fully independent working
directory at ../hotfix with the hotfix branch checked out into it —
separate files on disk, separate HEAD, but sharing the same underlying
.git object database as your original clone. Nothing about your original working
directory changes; you just cd into the new folder to work on the second branch, and
both stay checked out simultaneously. It's git's actual answer to "checkout two branches at
once" — something plain checkout or switch genuinely cannot do, since
both operate on a single working directory by design.
The trade-off is disk space and a bit of extra bookkeeping — each worktree is a full checked-out
copy of the files, and you have to remember to git worktree remove ../hotfix when
you're done, or it lingers. For a one-off "let me just peek at that branch," ordinary checkout is
still simpler; for recurring parallel work on two or more branches, worktrees remove the
stash-checkout-stash-pop cycle entirely.
Checking Out Branches in VS Code, JetBrains, and GitHub CLI
None of this requires a terminal. VS Code's status bar shows the current branch name at the
bottom left — clicking it opens a branch picker listing local and remote branches together, and
selecting a remote one performs the fetch-and-track sequence from earlier automatically, no
manual -b or --track needed — the shortest way to check out a branch
from GitHub if you'd rather not type anything. JetBrains IDEs (IntelliJ, WebStorm,
PyCharm) expose the same operation through the Git widget in the bottom-right corner —
"Checkout" on a remote branch in the branch list creates the local tracking branch in one click.
GitHub Desktop's branch dropdown does the equivalent for anyone who wants checkout without any
command line at all.
For pull requests specifically, the GitHub CLI collapses fetch, remote lookup, and checkout of a PR's branch into one command:
# Check out the branch behind pull request #42, fetching and tracking it automatically
gh pr checkout 42 gh pr checkout 42 is the fastest path when the thing you actually have is a PR
number, not a branch name — it resolves the PR to its source branch, fetches it (including from
a fork, if the PR came from one), and checks it out with tracking already configured, skipping
the "what's this branch even called" step entirely.
Mechanically, checking out a remote branch through GitHub's tooling is no different from any
other remote: the GUI or gh still fetches, still creates a local branch, still sets
the upstream. Only the interface changes — which is why the terminal commands above stay worth
knowing even if you check out a branch from GitHub through a picker every day. When a GUI
misbehaves, the fix is almost always one of the six terminal-level errors above.
After Checkout: Comparing What Actually Changed
Checking out a branch answers "what does this look like." The next question is usually "what's
actually different from what I had," and for a single file that's often easier to read visually
than as raw terminal +/- output.
# Pull one file's content off each branch into temp files
git show main:src/api/retry.ts > /tmp/a.txt
git show feature:src/api/retry.ts > /tmp/b.txt
Paste both versions into Diff Checker, a free Chrome extension — no repository access, no branch
awareness, just a two-pane text comparison. Its Monaco-based editor (the same engine behind VS
Code) lines the two versions up with word-level highlighting and syntax highlighting across 17
languages, toggles between side-by-side and unified view, and can collapse unchanged regions with
"Show Diff Only" so a long file doesn't bury the one function that actually changed between
branches. Everything runs client-side in the browser — nothing gets uploaded anywhere. It's a
genuinely useful complement to git diff main...feature, not a replacement for it:
reach for the terminal diff to see everything that changed across a whole branch, and reach for
the visual pane when one specific file's change needs a closer, more readable look. The same
pattern is covered in more depth in the
git diff between two files guide, and for
comparisons done inside an editor instead of a browser tab,
comparing two files in VS Code covers the
built-in diff viewer.
The same two-pane check is useful right after a cherry-pick onto the branch you just checked out, confirming the picked commit landed exactly as expected, and after an amended commit where you want to eyeball the final diff against the original before pushing.
Quick Reference: Every Checkout Command
Every git checkout example from this guide, side by side — every way to check out a branch
covered above, plus the specific-commit, tag, and single-file forms. If you just need the one
command for the common case, it's the git checkout -b feature origin/feature row.
| Command | What it does |
|---|---|
git fetch origin | Refreshes remote-tracking refs; required before checking out a newly created remote branch |
git checkout feature | DWIM: auto-creates a tracking branch if the name is unambiguous |
git checkout -b feature origin/feature | Explicit: creates a new local branch tracking a specific remote branch |
git checkout --track origin/feature | Same result as -b, spelled out explicitly as a tracking setup |
git switch feature | git 2.23+ equivalent of plain checkout for branch switching |
git switch -c feature origin/feature | git 2.23+ equivalent of checkout -b |
git checkout origin/feature | Detached HEAD at the remote-tracking ref's commit — no local branch created |
git checkout -b fix upstream/fix | Named-remote checkout when more than one remote has the same branch name |
git checkout <commit-hash> | Detached HEAD at a specific commit |
git checkout develop -- path/to/file | Restores one file from another branch — HEAD unchanged |
git checkout -b rescue-branch | Save commits made in detached HEAD before they become unreachable |
git fetch origin main:main | Fetch and write directly into a local branch in one step — useful in scripts |
git worktree add ../hotfix hotfix | Check out a second branch into its own working directory, in parallel |
gh pr checkout 42 | Fetch and check out a pull request's branch by PR number |
git fetch --prune | Clears stale remote-tracking refs for branches deleted on the server |
For the git-scm project's own reference on every flag — and the canonical, source-level answer to what is git checkout — see the official git-checkout documentation and the git-switch documentation.
Frequently Asked Questions
What's the difference between checking out a remote branch directly and creating a tracking branch?
Running git checkout origin/feature by itself puts you in detached HEAD —
you're looking at that commit with no local branch to commit onto. Creating a tracking
branch with git checkout -b feature origin/feature (or letting DWIM do it
automatically via git checkout feature) gives you a real local branch named
"feature" that knows its upstream is origin/feature, so plain git
pull and git push work without extra arguments. For anything beyond a
quick look, you want the tracking branch.
Can I just run git checkout <branch-name> to get a remote branch?
Yes, if two conditions hold: no local branch of that name already exists, and exactly one
remote has a branch with that name. Git's DWIM (do-what-I-mean) shortcut, in place since Git
1.8.4, then auto-creates a local tracking branch for you — git checkout feature
behaves like git checkout -b feature origin/feature. With two remotes both
carrying a "feature" branch, DWIM can't guess which one you mean and you have to spell it out
with git checkout -b feature origin/feature or upstream/feature.
What does git checkout -b actually mean?
Short version of the git checkout -b meaning: "-b" tells git to create a new
branch and check it out in one step, instead of switching to a
branch that already exists. git checkout -b feature creates "feature" from your
current HEAD; git checkout -b feature origin/feature creates it from the tip of
the remote-tracking branch "origin/feature" and sets that as its upstream automatically.
Without "-b", git checkout feature only works if the branch already exists (or
DWIM creates it for you).
Should I use git switch instead of git checkout?
For moving between branches, yes, if your git is 2.23 (August 2019) or newer —
git switch feature and git switch -c feature origin/feature are
narrower, harder-to-misuse versions of the same operations, and Git 2.36 (April 2022) dropped
the experimental warning. git checkout still has jobs git switch
doesn't cover, like restoring a single file's contents from another branch or detaching HEAD
at an arbitrary commit — those stay on git checkout or move to the companion
command git restore.
Do I need to run git fetch before checking out a remote branch?
Yes, if the branch was created or updated on the server after your last fetch — your local
remote-tracking refs (origin/*) only reflect what git last heard from the server, and
git checkout reads those local refs, not the server directly. Run git
fetch (or git fetch origin) first, or use git fetch --all to
update every configured remote at once, then checkout will see the branch.
What if there's a conflict with a local branch of the same name?
DWIM steps aside entirely when the name is already taken locally: git checkout
feature just switches to your local feature, ignoring
origin/feature even if the remote one is newer
or a completely unrelated line of work. Git only complains when you force the issue with
-b, which fails with "fatal: A branch named 'feature' already exists." Three ways
out, depending on what the local branch is worth: switch to it and run git pull
if it really is the same work, rename it with git branch -m feature feature-old,
or delete it with git branch -d feature (-D if it has unmerged
commits) and recreate it from the remote tip. git branch -vv tells you which
situation you're in — a local branch with no upstream listed is the sign that DWIM never ran.
What's the difference between origin and upstream?
Nothing in git treats either word specially — both are ordinary remote names, and the
difference is pure convention. origin is the remote git clone
created for you, the one you can push to; in a fork-based workflow that's your fork.
upstream is a second remote you add by hand with git remote add upstream
<url>, pointing at the original project you forked, which you fetch from but
usually can't push to. Once both exist and both carry a branch called fix, plain
git checkout fix is ambiguous, so name the remote:
git checkout -b fix upstream/fix checks it out from upstream, and
git checkout -b fix origin/fix checks it out from origin. Run
git remote -v to see what you actually have configured; spelling the remote out
makes it a deterministic checkout instead of a DWIM guess.