Home › DevOps › Git Pathspec Did Not Match — Fix It Fast
Beginner 6 min · September 23, 2026

Git Pathspec Did Not Match — Fix It Fast

Check your directory and spelling to fix pathspec errors, then fetch remote branches before you switch or restore the path..

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.

Follow
✓ Production
production tested
September 25, 2026
last updated
1,950
articles · all by Naren
Before you start⏱ 8 min
  • ✓Basic git add, commit, and branch commands
  • ✓A cloned repo with a remote configured
  • ✓Comfort running commands in the terminal
 ● Production Incident 🔎 Debug Guide
⚡Quick Answer
  • fatal: pathspec did not match means Git can't find the branch, file, or path you named in the current repo state.
  • Fix typos first: run git branch -a and git status to compare your spelling against real branch and file names.
  • If you're in the wrong folder or the branch is remote-only, cd to the repo root and run git fetch --all --prune first.
  • Use git switch for branches and git restore for files; sparse-checkout users must check git sparse-checkout list for hidden paths.
✦ Definition~90s read
What is Git Pathspec Did Not Match Fix?

A pathspec is the string you give Git to name a branch, tag, file, or pattern: the feature-login in git switch feature-login, the src/App.tsx in git add src/App.tsx. Git matches it against two catalogs — refs (branches, tags, remote-tracking names) and paths (tracked files plus working-tree entries) — scoped to your current repo state, directory, and sparse cone. fatal: pathspec did not match means the lookup found nothing in either catalog, so Git stops instead of guessing.

★
Think of Git as a librarian who only fetches books that are on the shelf list.

That strictness is a feature with five common triggers. Typos miss both catalogs by a character. Wrong directories shift relative paths so a valid repo-root path points nowhere from a subfolder. Remote-only branches live on the server but not in your local ref list until fetch.

Checkout ambiguity sends a filename down the branch-lookup path. Sparse-checkout removes on-disk paths from the searchable set while keeping them in history.

What it is NOT matters for triage. It's not an auth failure — auth errors name permissions or keys. It's not repo corruption — corruption names objects, not pathspecs. It's not a merge conflict — conflicts list files, they don't claim names are unknown. And it's not fixed by re-cloning unless the clone also corrects the name, directory, or fetch you skipped.

Think of it as a library lookup. The catalog is current refs plus visible files from where you stand. Spell the title exactly, stand in the right room, and make sure the delivery truck was unloaded first.

Plain-English First

Think of Git as a librarian who only fetches books that are on the shelf list. If you ask for Harry Potter and the Sorceror's Stone with a typo, or you ask for a book that's still in the delivery truck, she says she can't find it. The pathspec error is that polite refusal. You either misspelled the title, you're standing in the wrong library branch, or the book hasn't been shelved yet. Fix the name, go to the right room, or unload the truck first.

You type git checkout feature-login, hit enter, and Git answers: error: pathspec 'feature-login' did not match any file(s) known to git. Or you run git add src/App.tsx and get the same refusal for a file you swear exists. Nothing is broken, yet nothing moves forward.

The reflex is to retry with sudo, re-clone the repo, or force-push something. None of that helps, because the error is about naming and visibility, not permissions. Git searched its current index of branches and files, didn't find your string, and stopped rather than guess.

Five causes cover nearly every case: a typo in the branch or path, running the command from the wrong directory, a branch that exists only on the remote and was never fetched, mixing up git switch and git checkout semantics, and sparse-checkout hiding the path from your working tree.

This guide shows how to read which cause you've hit, the exact commands that confirm it, and the minimal fix for each. You'll clear the error in under two minutes and stop re-cloning repos to solve a spelling problem.

Typo'd Branch or Path: Read the Error Closely

Most pathspec failures are spelling errors wearing a scary message. feature-login versus feature_logins, src/App.tsx versus src/app.tsx on a case-sensitive filesystem, release-2.14 versus release-2.14.1 — Git does fuzzy nothing. It compares your string byte for byte against known refs and paths, and one wrong character is a total miss. The error helpfully echoes your string back; compare that echo to reality instead of retyping the same typo.

Confirm with two read-only commands before changing anything. git branch -a shows every local and remote-tracking branch Git currently knows, and git status --short shows tracked and untracked paths in your working tree. Pipe the first through grep with a fragment you trust: git branch -a | grep login surfaces every login-flavored branch so you can pick the exact name. For files, ls the directory and use tab completion rather than memory.

Case trips up teams that mix macOS and Linux. macOS checkouts often behave case-insensitively while CI runs case-sensitively, so App.tsx works on your laptop and fails in the pipeline. Globs trip up the shell before Git ever sees them: unquoted src/*.tsx expands locally and vanishes if nothing matches. Quote your patterns.

Make the fix boring: copy the exact name from branch -a or ls output, paste it into your command, and verify with git rev-parse --verify <name>. You'll clear the majority of pathspec reports right here.

BASH
1
2
3
4
5
6
7
8
9
10
11
12
13
# See what Git actually knows (local + remote-tracking)
git branch -a | grep -i login

# Verify an exact ref before checking out
git rev-parse --verify feature-login

# Compare your path against the working tree
git status --short
ls src/

# Quote globs so the shell doesn't eat them
git add 'src/*.tsx'
git checkout main -- 'src/App.tsx'
📊 Production Insight
Deploy scripts that paste branch names from tickets fail this way monthly. Pull the name from a variable and verify with rev-parse so typos fail loudly with close matches.
🎯 Key Takeaway
Copy the ref from git branch -a output instead of memory. One character decides match versus pathspec.

Wrong Directory: You're Not Where You Think

Git resolves relative pathspecs against your current directory, not the repo root. Running git add src/App.tsx from repo-root works; running it from repo-root/src fails because Git looks for src/src/App.tsx. Running any branch command from outside the repo fails differently but just as confusingly. Monorepos multiply the risk: packages/api versus apps/api look alike until you're in the wrong one.

Diagnose location before syntax. pwd tells you where the shell is, git rev-parse --show-toplevel tells you where the repo root is, and git status tells you which directory's changes Git sees. If those disagree with your assumption, you've found the bug. Nested repos and worktrees add a twist: git rev-parse --git-dir reveals which .git you're actually talking to when folders contain more than one checkout.

The fix is to anchor yourself. cd to the output of git rev-parse --show-toplevel and rerun the command, or prefix with git -C <root> to run from the root without moving: git -C ~/code/app status. For scripts, use absolute repo-root-relative paths or :/ prefixes that anchor to the root regardless of cwd: git add :/src/App.tsx works from any subdirectory.

Add pwd and git rev-parse --show-toplevel to your deploy logs. That two-line context has resolved more pathspec mysteries than every Stack Overflow thread combined.

BASH
1
2
3
4
5
6
7
8
9
10
11
# Where are you vs where is the repo root?
pwd
git rev-parse --show-toplevel
git rev-parse --git-dir

# Run from the root without moving
git -C "$(git rev-parse --show-toplevel)" status --short

# Anchor paths to the repo root from any subdir
git add :/src/App.tsx
git checkout main -- :/packages/api/server.ts
📊 Production Insight
CI steps that set working-directory to a subfolder break root-relative adds. Log pwd at each step so the failing path resolves correctly.
🎯 Key Takeaway
Resolve paths from the repo root. Check pwd against rev-parse --show-toplevel before blaming the ref.

Unpushed and Remote-Only Branches: Fetch First

A branch that exists on GitHub but was never fetched doesn't exist for your local Git. Fresh clones fetch only the default branch plus remote-tracking refs at clone time; branches created afterward are invisible until git fetch updates your remote-tracking list. Deleting a local branch while it still exists remotely creates the mirror problem: you see origin/feature but no local feature, and bare git checkout feature may or may not guess the tracking setup depending on your config.

Confirm visibility with git branch -a, which lists both local branches and origin/* remote-tracking refs, and git ls-remote origin to see what's on the server without fetching. If ls-remote shows the branch but branch -a doesn't, you're simply behind: fetch. If both show it but checkout still fails, your spelling or your checkout.mode config is the culprit, not the network.

Fetch deliberately, then track explicitly. git fetch --all --prune refreshes every remote and drops stale refs that confuse guessing. Then git switch --track origin/feature-login creates the local branch with its upstream set, which beats git checkout's sometimes-magical guessing. For one-shot checkouts, git fetch origin feature-login:feature-login creates the local ref directly.

Clone flags matter at scale. Pipelines that use --depth 1 or --single-branch to save time can't see other branches by design. Either fetch with --unshallow or widen the clone when you need full ref access.

BASH
1
2
3
4
5
6
7
8
9
10
11
12
13
# What's on the server vs what you have locally?
git branch -a | grep release
git ls-remote origin | grep release

# Refresh all remotes and drop stale refs
git fetch --all --prune

# Create a local tracking branch explicitly
git switch --track origin/feature-login

# One-shot fetch into a local ref
git fetch origin hotfix-404:hotfix-404
git switch hotfix-404
📊 Production Insight
Shallow CI clones cause this weekly. The branch is on the remote, the runner just never fetched it — widen the fetch instead of debugging auth.
🎯 Key Takeaway
If ls-remote shows it but branch -a doesn't, fetch. Then track explicitly with switch --track.

Git Switch vs Checkout: Use the Right Tool

git checkout does three jobs — switch branches, restore files, and create branches — and guesses which one you meant. That guessing is a classic pathspec source: git checkout report.txt tries to switch to a branch named report.txt before it tries to restore the file, and fails with pathspec when no such branch exists. Modern Git split the jobs: git switch moves branches, git restore recovers files, and checkout remains as the overloaded legacy.

Use the split commands and the ambiguity vanishes. git switch feature-login can only mean a branch, so a typo there is unambiguously a branch problem. git restore --source=main -- src/App.tsx can only mean a file, so a typo there is unambiguously a path problem. Each error message gets shorter and more accurate because the command already declared its intent.

When you must use checkout, disambiguate with --. The double dash tells Git everything after it is a path: git checkout main -- src/App.tsx checks out the file from main instead of hunting for a branch named App.tsx. Without the separator, branch names that look like paths (or paths that look like branches) trigger guess failures that read exactly like typos.

Teach your fingers the new pair and alias the old habit away. switch plus restore covers every checkout use with clearer errors, and your future pathspec reports will name the right category on the first line.

BASH
1
2
3
4
5
6
7
8
9
10
11
# Branches only: unambiguous
 git switch main
git switch -c feature-login

# Files only: unambiguous
git restore --source=main -- src/App.tsx
git restore --staged -- src/App.tsx

# Legacy checkout, disambiguated with --
git checkout main -- src/App.tsx
git checkout -b hotfix-404 origin/hotfix-404
📊 Production Insight
Runbooks written for checkout confuse new hires when a filename matches a branch. Rewrite them with switch and restore so errors point at the right object.
🎯 Key Takeaway
Switch moves branches, restore recovers files. The split removes checkout's guessing that causes false pathspecs.

Sparse-Checkout Hiding Paths: Files You Can't See

Sparse-checkout lets a monorepo show only part of the tree in your working copy. Files outside the cone still exist in history, but your disk doesn't have them and pathspec commands that name them fail as if you'd typo'd. git checkout main -- services/billing/api.ts errors when your cone covers only apps/web, even though the path is perfectly valid in the repo. The confusion peaks because git ls-tree shows the file while ls doesn't.

Check the cone before the spelling. git sparse-checkout list prints the directories Git materializes, and git sparse-checkout status shows whether the feature is enabled. Compare that list to your target path: if the path falls outside every cone entry, no spelling fix will help until you widen the cone. git check-ignore won't explain this one — the file isn't ignored, it's just not checked out.

Widen deliberately. git sparse-checkout add services/billing pulls one more directory into the working tree without disabling the feature. git sparse-checkout set with a new pattern list replaces the cone when you're re-scoping. git sparse-checkout disable restores the full tree when you need everything, at the cost of disk and checkout time on large repos.

Document cone requirements in the repo readme. When onboarding says this service needs apps/web plus services/billing, every pathspec report from a fresh sparse clone answers itself.

BASH
1
2
3
4
5
6
7
8
9
10
11
# Is sparse-checkout hiding your path?
git sparse-checkout status
git sparse-checkout list

# File in history but not on disk?
git ls-tree -r HEAD --name-only | grep billing/api
ls services/billing/ 2>&1

# Widen the cone or restore the full tree
git sparse-checkout add services/billing
git sparse-checkout disable
📊 Production Insight
Monorepo CI with cone-mode checkouts fails adds for untouched services. Either widen the cone in the job or run the step from a full checkout.
🎯 Key Takeaway
If ls-tree shows the path but ls doesn't, the cone hides it. Add the directory before retrying.

Stale Refs and Deleted Branches: Prune the Ghosts

Remote branches get deleted after merges, but your local remote-tracking refs linger until pruned. Then git branch -a shows origin/feature-login, you try to switch, and the fetch you just ran deleted that tracking ref because the upstream is gone — pathspec on a branch you can still see in old output. The reverse haunts restores: a file deleted on main still exists in your head's history, and checking it out from the wrong ref fails.

Treat ref lists as perishable. git fetch --prune (or --all --prune for every remote) deletes local tracking refs whose upstream vanished, so branch -a reflects the server instead of last month. git branch -vv shows which local branches track deleted upstreams with a gone marker — those need deletion or re-pointing, not checkout retries. git remote prune origin does the same cleanup when you want it without fetching.

For files, confirm existence on the exact ref you're reading from. git ls-tree -r main --name-only | grep App.tsx proves the path lives on main right now; without that proof you're restoring from memory. Deleted files restore fine from the commit before deletion with git restore --source=HEAD~1 -- <path>, but only once you've named a ref that actually contains them.

Schedule pruning so ghosts don't accumulate. A fetch with prune in your daily pull habit, plus gone-branch cleanup monthly, keeps every pathspec report about real names instead of deleted ones.

⚠ Don't Chase Ghost Refs
If branch -a shows a remote branch that ls-remote doesn't, it's already deleted upstream. Prune first, then pick a live ref instead of retrying the dead name.
📊 Production Insight
Post-merge cleanup deletes remote branches within hours. Pipelines that cache branch -a output across jobs chase ghosts — fetch with prune in the same job.
🎯 Key Takeaway
Prune stale refs before trusting branch -a. Ghost entries cause pathspecs that no spelling fix can clear.
● Production incidentPOST-MORTEMseverity: high

The Typo'd Deploy Branch That Delayed Release 40 Minutes

Symptom
CI failed at the checkout step with pathspec 'release-2.14' did not match any file(s) known to git on every runner. The release pipeline blocked, Slack filled with re-run attempts, and two engineers re-cloned the repo locally to prove the branch existed — it did, under a slightly different name. No code was broken; the pipeline simply couldn't find the ref it was told to deploy.
Assumption
The team assumed the branch name from the ticket title was exact and that runners had stale caches. They cleared caches, re-ran jobs, and blamed the CI image. Nobody compared the script's hardcoded name against git branch -a output until the third failure, because the names differed by only two characters at the end.
Root cause
The release branch was cut as release-2.14.1 to include a hotfix, but the deploy script still referenced release-2.14. The short name matched no local branch and no remote-tracking branch after fetch, so Git refused with pathspec. A prior git fetch --prune had also removed a stale short-lived branch of the old name, which made the error look like a fetch problem rather than the typo it was. The script had no validation that the ref exists before checkout.
Fix
The script now resolves the ref before checkout with git fetch --all --prune followed by git rev-parse --verify, and fails with the list of close matches from git branch -a when it misses. The branch name moved to a CI variable instead of a hardcoded string, and the pipeline prints git branch -a plus pwd at the start of the checkout step. The blocked release was unblocked by correcting the variable to release-2.14.1 and re-running once.
Key lesson
  • Never hardcode branch names in scripts: take them from variables and verify with rev-parse before checkout so typos fail with helpful output.
  • Print pwd, git branch -a, and git status at the start of deploy steps. That context turns a 40-minute mystery into a 2-minute spelling fix.
  • Treat pathspec as a naming error first, not an infrastructure error. Compare your string to real refs before clearing caches or rebuilding runners.
Production debug guideFive checks that pinpoint the misspelled, misplaced, or unfetched ref in under two minutes.5 entries
Symptom · 01
pathspec did not match on git checkout or git switch
→
Fix
List what Git actually knows with git branch -a and git status, then compare your spelling character by character. Run git fetch --all --prune to refresh remote refs, then git rev-parse --verify <name> to test the exact string. If it verifies after fetch, check out the corrected full name.
Symptom · 02
pathspec did not match on git add or git restore
→
Fix
Confirm your location with pwd and git rev-parse --show-toplevel, then list files with git status --short and ls on the path. Run git diff --name-only HEAD to see tracked changes. If the file is untracked-but-ignored, check git check-ignore -v <path> before retrying the add from the repo root.
Symptom · 03
Branch exists on GitHub but not locally
→
Fix
Fetch it explicitly with git fetch origin <branch>:<branch> or git fetch --all --prune, then verify with git branch -a | grep <branch>. Switch with git switch --track origin/<branch> or git checkout --track origin/<branch>. Never assume clone pulled every branch.
Symptom · 04
Path exists in repo but sparse-checkout hides it
→
Fix
Check visibility with git sparse-checkout list and git sparse-checkout status. If your path is outside the cone, add it with git sparse-checkout add <dir> or disable with git sparse-checkout disable. Then run git checkout <branch> -- <path> again.
Symptom · 05
git checkout <branch> -- <path> still fails after fetch
→
Fix
Separate the ref from the path with git checkout <branch> -- <path> using the double dash, and quote globs like 'src/*.tsx'. Verify the path exists on that branch with git ls-tree -r <branch> --name-only | grep <path>. If it's absent there, you've got the wrong branch, not the wrong syntax.
Pathspec Causes Compared
Root CauseHow to ConfirmFixPrevention
Typo in branch or file namegit branch -a plus git status show no such name; rev-parse failsCopy exact name from listing; quote globsUse tab completion; verify with rev-parse in scripts
Wrong working directorypwd differs from rev-parse --show-toplevel; path resolves oddlycd to repo root or use git -C and :/ anchored pathsLog pwd in CI steps; use root-anchored paths
Remote-only branch never fetchedls-remote shows it but branch -a doesn'tgit fetch --all --prune then switch --track origin/branchFetch with prune in jobs; avoid single-branch clones
Sparse-checkout hides the pathls-tree shows path but ls doesn't; sparse-checkout list excludes itgit sparse-checkout add <dir> or disableDocument required cone dirs in readme
⚙ Quick Reference
5 commands from this guide
FileCommand / CodePurpose
git branch -a | grep -i loginTypo'd Branch or Path
pwdWrong Directory
git branch -a | grep releaseUnpushed and Remote-Only Branches
git switch mainGit Switch vs Checkout
git sparse-checkout statusSparse-Checkout Hiding Paths

Key takeaways

1
Pathspec means Git can't find the name you gave among its current refs and paths.
2
List real names with branch -a and status before retyping the command.
3
Fetch with prune before assuming a remote branch is missing.
4
Run path commands from the repo root or anchor them with :/ prefixes.
5
Prefer switch for branches and restore for files to kill checkout ambiguity.
6
Check sparse-checkout cones when history shows a path your disk doesn't.

Common mistakes to avoid

5 patterns
×

Retyping the same typo harder instead of listing refs

Symptom
Three identical checkout retries, same pathspec, growing frustration while the correct name sits one grep away.
Fix
Run git branch -a | grep <fragment> and copy the exact ref. Verify with git rev-parse --verify before checkout.
×

Running path commands from a subdirectory

Symptom
git add src/App.tsx fails from inside src/ because Git looks for src/src/App.tsx relative to cwd.
Fix
Check pwd versus git rev-parse --show-toplevel, then run from the root or anchor with :/src/App.tsx.
×

Assuming clone fetched every branch

Symptom
Branch visible on GitHub errors locally with pathspec on a fresh clone or shallow CI checkout.
Fix
Run git fetch --all --prune, then git switch --track origin/<branch> to create the local tracking branch.
×

Using checkout for files without the -- separator

Symptom
git checkout App.tsx hunts for a branch named App.tsx and reports pathspec instead of restoring the file.
Fix
Use git restore --source=<ref> -- <path>, or git checkout <ref> -- <path> with the double dash.
×

Forgetting sparse-checkout cones on monorepos

Symptom
Path exists in history (ls-tree shows it) but commands fail and ls shows no such file.
Fix
Run git sparse-checkout list, then git sparse-checkout add <dir> to materialize the missing directory.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What does fatal: pathspec did not match mean?
Q02SENIOR
How do you tell a typo from an unfetched remote branch?
Q03SENIOR
Why does the same git add work in one folder but fail in another?
Q04JUNIOR
When should you use git switch and git restore instead of git checkout?
Q05SENIOR
A path exists in git ls-tree but checkout fails. What's going on?
Q01 of 05JUNIOR

What does fatal: pathspec did not match mean?

ANSWER
Git couldn't find the branch, file, or pattern you named among the refs and paths it currently knows. It's a naming or visibility problem — typo, wrong directory, unfetched branch, or hidden path — not a permissions or corruption problem.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
Why does Git say pathspec did not match for a branch I see online?
02
Why does git add fail for a file that clearly exists?
03
What's the -- in git checkout main -- file for?
04
Should I use git switch or git checkout for branches?
05
How does sparse-checkout cause pathspec errors?
06
How do I stop chasing deleted remote branches?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Everything here is grounded in real deployments.

Follow
✓ Verified
production tested
September 25, 2026
last updated
1,950
articles · all by Naren
🔥

That's Git. Mark it forged?

6 min read · try the examples if you haven't

←
Previous
Ansible SSH Connection Failed Fix
52 / 53 · Git
Next
Git LF CRLF Warning Fix
→