This error occurs when Git cannot resolve a reference (branch name, tag, or commit) to a valid commit object. It typically happens when a branch name conflicts with another Git reference like a tag pointing to a non-commit object, or when trying to checkout a reference that doesn't exist. The fix involves using fully qualified reference paths or resolving the naming conflict.
The "fatal: Cannot switch branch to a non-commit" error means Git tried to switch to a branch or reference but found that the name doesn't resolve to a commit object. In Git's object model, a commit is the fundamental unit that represents a snapshot of your repository at a point in time. Branches must point to commits, not to other object types like trees or blobs. This error commonly occurs in these scenarios: 1. **Tag and branch name collision**: You have a tag with the same name as a branch you're trying to checkout, and that tag points to a tree or blob object instead of a commit. 2. **Corrupt or unusual reference**: A reference exists that points to a non-commit object (like a tree object), perhaps created by a low-level Git operation or a bug. 3. **Numeric branch names**: Branch names that look like commit hashes (e.g., "12382") can confuse Git's reference resolution logic, especially when Git tries to interpret them as short commit hashes. 4. **Remote branch not fetched**: When trying to checkout a branch that exists on a remote but hasn't been fetched, Git may fail to resolve the reference properly. Git's checkout command first tries to interpret your argument as a branch name. If that fails, it tries various other interpretations (tag, commit hash, etc.). When all interpretations fail to produce a valid commit, you get this error.
When Git is confused about which reference you mean, use the full path to be explicit:
# For a local branch
git checkout refs/heads/<branch-name>
# For a remote tracking branch
git checkout refs/remotes/origin/<branch-name>
# This puts you in detached HEAD state
# Then create a local branch if needed:
git checkout -b <branch-name>Example:
# Instead of:
git checkout feature-123
# Use:
git checkout refs/heads/feature-123
# Or for remote:
git checkout refs/remotes/origin/feature-123
git checkout -b feature-123Using full reference paths bypasses Git's automatic reference resolution and directly specifies which object you want.
If the branch exists on a remote, use the --track flag to explicitly set up tracking:
# Fetch latest from remote first
git fetch origin
# Checkout with explicit tracking
git checkout --track origin/<branch-name>
# Or use the -b flag to create a local branch
git checkout -b <branch-name> --track origin/<branch-name>Alternative with git switch (Git 2.23+):
# Git switch handles this more gracefully
git switch <branch-name>
# Or explicitly create and track
git switch -c <branch-name> --track origin/<branch-name>The --track flag ensures Git sets up the proper tracking relationship and doesn't get confused about reference types.
A common cause is having a tag with the same name as your branch that points to a non-commit object:
# Check what type of object your reference points to
git cat-file -t <name>
# See all references matching the name
git show-ref <name>
# Check if there's a conflicting tag
git tag -l | grep <name>
# Examine what the tag points to
git rev-parse --verify tags/<name>If you find a problematic tag:
# Delete the conflicting tag
git tag -d <tag-name>
# If it's on the remote too
git push origin --delete <tag-name>
# Now checkout should work
git checkout <branch-name>To see the object type:
# This shows commit, tree, blob, or tag
git cat-file -t refs/tags/<name>
git cat-file -t refs/heads/<name>If the branch exists on the remote but hasn't been fetched, Git cannot resolve it:
# Fetch all branches from all remotes
git fetch --all
# Fetch from specific remote
git fetch origin
# See all available branches now
git branch -a
# Check if your branch is listed
git branch -a | grep <branch-name>After fetching, try checkout again:
# Standard checkout
git checkout <branch-name>
# If still failing, use explicit remote reference
git checkout -b <branch-name> origin/<branch-name>For shallow clones:
# If you did a shallow clone, you may need to unshallow
git fetch --unshallow
# Then fetch all branches
git fetch --allDebug the reference resolution to understand what's happening:
# See what Git resolves the reference to
git rev-parse <name>
# Get the full symbolic reference
git rev-parse --symbolic-full-name <name>
# Check if it's a valid commit
git rev-parse --verify <name>^{commit}
# See all matching references
git for-each-ref --format='%(refname) %(objecttype) %(objectname)' | grep <name>If the reference points to a tree object:
# This shows "tree" instead of "commit"
git cat-file -t $(git rev-parse <name>)
# Find the commit that contains this tree
git log --all --full-history -- $(git rev-parse <name>)Understanding what the reference points to helps determine the appropriate fix.
Branch names that look like numbers or short commit hashes cause confusion:
# If your branch is named "12382", Git might think it's a commit hash
# Always use explicit reference paths for numeric names:
git checkout refs/heads/12382
# Or prefix with origin for remote branches
git checkout -b 12382 origin/12382Best practice - avoid numeric-only branch names:
# Instead of:
git checkout -b 12382
# Use a descriptive prefix:
git checkout -b issue-12382
git checkout -b feature/12382
git checkout -b ticket-12382If you must use numeric names:
# Always use refs/heads/ prefix
git checkout refs/heads/12382
# Or use -- to separate branch from paths
git checkout -- 12382Git 2.23+ introduced git switch which handles branch switching more cleanly:
# Switch to an existing branch
git switch <branch-name>
# Create and switch to new branch
git switch -c <new-branch>
# Create branch tracking remote
git switch -c <branch-name> --track origin/<branch-name>
# Switch to a detached HEAD at a commit
git switch --detach <commit-hash>Advantages of git switch:
- Clearer error messages for invalid references
- Separate commands for switching vs restoring files
- Less ambiguity in argument parsing
If git switch also fails:
# The error message may be more descriptive
git switch feature-123
# fatal: invalid reference: feature-123
# Check what references exist
git show-ref | grep feature-123If the branch reference is corrupted, recreate it from a known good commit:
# Find the commit the branch should point to
git log --all --oneline | head -20
git log origin/<branch-name> --oneline -5
# Delete the problematic local branch
git branch -D <branch-name>
# Recreate from the correct commit
git branch <branch-name> <commit-hash>
git checkout <branch-name>
# Or recreate from remote
git checkout -b <branch-name> origin/<branch-name>If the branch never existed locally:
# Verify it exists on remote
git ls-remote origin | grep <branch-name>
# Fetch and create local tracking branch
git fetch origin <branch-name>:<branch-name>
git checkout <branch-name>Understanding Git Object Types:
Git has four object types:
- commit: A snapshot of the repository with metadata (author, message, parent commits)
- tree: A directory listing that maps names to blobs and other trees
- blob: File contents without any metadata
- tag: An annotated tag object that can point to any other object
Branches must always point to commits. When something creates a reference pointing to a tree or blob, Git cannot check it out as a branch.
How Reference Resolution Works:
When you run git checkout foo, Git searches in this order:
1. refs/heads/foo (local branch)
2. refs/remotes/origin/foo (remote tracking branch)
3. refs/tags/foo (tag)
4. Partial SHA-1 match (commit hash)
If "foo" matches multiple references or matches a non-commit object, the error occurs.
Debugging with GIT_TRACE:
GIT_TRACE=1 git checkout <branch-name> 2>&1 | head -50This shows exactly how Git tries to resolve the reference.
The --orphan Flag:
The --orphan flag creates a new branch with no parent commits:
# Create an orphan branch (starts fresh history)
git checkout --orphan new-history
# After creating, your working tree has staged files but no commits yet
git status # Shows all files as "new file"
# To start truly empty:
git rm -rf .
git commit --allow-empty -m "Initial commit on orphan branch"Note: git switch --orphan <branch> automatically removes all files, unlike git checkout --orphan.
Fixing Corrupted References:
If Git's reference database is corrupted:
# Check for reference problems
git fsck --full
# List all references and their targets
git for-each-ref
# Manually fix a reference (advanced)
# Edit .git/refs/heads/<branch> to contain correct commit SHA
# Or delete and recreate
rm .git/refs/heads/<branch>
git branch <branch> <correct-commit-sha>Partial Clone Considerations:
With --filter=blob:none clones, some objects may not be present:
# Fetch missing objects for a specific reference
git fetch origin <branch>
# Or convert to full clone
git fetch --unshallow
git fetch --allCI/CD Pipeline Fixes:
In CI environments, ensure proper fetch before checkout:
# GitHub Actions example
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history
ref: ${{ github.head_ref }}
# Or manually
- run: |
git fetch origin +refs/heads/*:refs/remotes/origin/*
git checkout ${{ github.head_ref }}kex_exchange_identification: Connection closed by remote host
Connection closed by remote host when connecting to Git server
fatal: unable to access: Proxy auto-configuration failed
How to fix 'Proxy auto-configuration failed' in Git
fatal: unable to access: Authentication failed (proxy requires basic auth)
How to fix 'Authentication failed (proxy requires basic auth)' in Git
fatal: unable to access: no_proxy configuration not working
How to fix 'no_proxy configuration not working' in Git
fatal: unable to read tree object in treeless clone
How to fix 'unable to read tree object in treeless clone' in Git