How to fix fatal: Cannot switch branch to a non-commit in Git

GitINTERMEDIATEMEDIUMUpdated Dec 6, 2025· 225 views

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.

Quick fix

Fixes fatal: Cannot switch branch to a non-commit

# 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>
  1. Use the fully qualified reference path
  2. Use git checkout with --track flag
  3. Check for conflicting tags or references
  4. Fetch remote branches first
git — error
# 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>
fatal: Cannot switch branch to a non-commit

What this error means

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.

Typical symptoms

  • Running 'git checkout <branch>' produces 'fatal: Cannot switch branch to a non-commit' error
  • Attempting to checkout a branch with a numeric name fails unexpectedly
  • git switch command fails with a similar 'invalid reference' error
  • Branch exists in 'git branch -a' but cannot be checked out
  • CI/CD pipeline fails when trying to checkout a specific branch
  • Team members can checkout the branch but you cannot
  • Error mentions the specific branch name followed by 'is not a commit'

Common causes

  1. A tag with the same name as the branch points to a non-commit object (tree or blob)
  2. The branch name looks like a short commit hash and Git misinterprets it
  3. The reference points to an invalid or corrupted object in the Git database
  4. Remote branch hasn't been fetched locally and can't be resolved
  5. Using an ambiguous reference that Git cannot resolve to a single commit
  6. The branch was created incorrectly using low-level Git commands
  7. Repository was partially cloned or has missing objects
  8. Conflict between local and remote references with the same name

How to fix "fatal: Cannot switch branch to a non-commit"

1Use the fully qualified reference path

When Git is confused about which reference you mean, use the full path to be explicit:

bash
# 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:

bash
# 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-123

Using full reference paths bypasses Git's automatic reference resolution and directly specifies which object you want.

2Use git checkout with --track flag

If the branch exists on a remote, use the --track flag to explicitly set up tracking:

bash
# 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+):

bash
# 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.

3Check for conflicting tags or references

A common cause is having a tag with the same name as your branch that points to a non-commit object:

bash
# 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:

bash
# 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:

bash
# This shows commit, tree, blob, or tag
git cat-file -t refs/tags/<name>
git cat-file -t refs/heads/<name>
4Fetch remote branches first

If the branch exists on the remote but hasn't been fetched, Git cannot resolve it:

bash
# 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:

bash
# Standard checkout
git checkout <branch-name>

# If still failing, use explicit remote reference
git checkout -b <branch-name> origin/<branch-name>

For shallow clones:

bash
# If you did a shallow clone, you may need to unshallow
git fetch --unshallow

# Then fetch all branches
git fetch --all
5Verify the reference resolves to a commit

Debug the reference resolution to understand what's happening:

bash
# 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:

bash
# 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.

6Handle numeric branch names carefully

Branch names that look like numbers or short commit hashes cause confusion:

bash
# 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/12382

Best practice - avoid numeric-only branch names:

bash
# Instead of:
git checkout -b 12382

# Use a descriptive prefix:
git checkout -b issue-12382
git checkout -b feature/12382
git checkout -b ticket-12382

If you must use numeric names:

bash
# Always use refs/heads/ prefix
git checkout refs/heads/12382

# Or use -- to separate branch from paths
git checkout -- 12382
7Use git switch instead of git checkout

Git 2.23+ introduced git switch which handles branch switching more cleanly:

bash
# 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:

bash
# 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-123
8Recreate the branch from a known good commit

If the branch reference is corrupted, recreate it from a known good commit:

bash
# 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:

bash
# 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>

Advanced notes

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:

bash
GIT_TRACE=1 git checkout <branch-name> 2>&1 | head -50

This shows exactly how Git tries to resolve the reference.

The --orphan Flag:

The --orphan flag creates a new branch with no parent commits:

bash
# 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:

bash
# 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:

bash
# Fetch missing objects for a specific reference
git fetch origin <branch>

# Or convert to full clone
git fetch --unshallow
git fetch --all

CI/CD Pipeline Fixes:

In CI environments, ensure proper fetch before checkout:

yaml
# 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 }}

Frequently asked questions

What does "fatal: Cannot switch branch to a non-commit" mean?
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.
How do I fix "fatal: Cannot switch branch to a non-commit"?
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.
What causes "fatal: Cannot switch branch to a non-commit"?
A tag with the same name as the branch points to a non-commit object (tree or blob) The branch name looks like a short commit hash and Git misinterprets it The reference points to an invalid or corrupted object in the Git database
Is the "fatal: Cannot switch branch to a non-commit" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading