src refspec X does not match any means Git looked for a local ref called X to push and found nothing. The remote is irrelevant here; the problem is local.
git branch -a
git log --oneline -1
Cause 1: No commits yet
git branch
# (no output)
git log --oneline -1
# fatal: your current branch 'main' does not have any commits yet
A branch is a pointer to a commit. With no commits there is no branch, only a HEAD that names one which does not exist yet.
git add is not enough. Staging does not create a commit:
git status --short
# A README.md <- staged, still no commit
git commit -m "Initial commit"
git push -u origin main
This is by far the most common cause, and it usually happens when following setup instructions and skipping the commit step.
Cause 2: The branch is called master
git init created master on older Git, and you are pushing main.
git branch
# * master
git branch -M main
git push -u origin main
-M is --move --force, which renames the current branch and overwrites the target name if it exists.
Set the default for new repositories so it stops recurring:
git config --global init.defaultBranch main
Git 2.28 added that setting, and prints a hint about it on git init.
Cause 3: A typo, or a detached HEAD
git branch --show-current
Empty output means detached HEAD: you are on a commit, not a branch, usually after git checkout <sha> or during a rebase.
git switch -c main
git push -u origin main
Pushing something other than a branch
The refspec is <source>:<destination>, and the source has to exist locally:
git push origin main # branch
git push origin v1.4.2 # tag
git push origin HEAD:main # current commit to remote main
git push origin a1b2c3d:main # a specific commit
HEAD:main is useful in CI, where the checkout is often detached and there is no local branch to name:
script:
- git push origin HEAD:main
A misspelled tag gives exactly the same error, so check with git tag -l.
Deleting a remote branch
The old syntax looks like this error waiting to happen:
git push origin :feature/old # empty source means delete
git push origin --delete feature/old
The second is clearer and does not risk a typo becoming a push.
In CI
Most CI systems check out a detached HEAD at a specific commit, so there is no local branch:
git branch --show-current
# (empty)
# GitLab CI
script:
- git push origin HEAD:$CI_COMMIT_BRANCH
# GitHub Actions: ask for a branch checkout
- uses: actions/checkout@v4
with:
ref: ${{ github.head_ref || github.ref_name }}
Also note that actions/checkout defaults to fetch-depth: 1, a shallow clone, which is fine for pushing a new commit and not for anything needing history.
A checklist
git branch -a. Nothing listed → no commits exist.git log --oneline -1. "does not have any commits yet" confirms it.git addalone is not enough. Commit.- Branch is
master→git branch -M main. git config --global init.defaultBranch mainto stop it recurring.git branch --show-currentempty → detached HEAD.git switch -c main.- Pushing a tag → confirm it exists with
git tag -l. - In CI →
git push origin HEAD:<branch>, since the checkout is detached.
Frequently Asked Questions
Why does git push fail on a brand new repository?
Because there are no commits, and therefore no branch to push. A branch in Git is nothing more than a pointer to a commit, so until you make one, main does not exist even though git status shows you on it. Staging files with git add does not create a commit. Run git commit -m "Initial commit" first, then git push -u origin main. This is the single most common cause of this error.
What does the -M flag do in git branch -M main?
It is shorthand for --move --force: rename the current branch to main, overwriting an existing branch of that name if there is one. It is the standard fix when git init created master and your remote expects main. Because it forces, it will overwrite a local main that already exists, so on a repository with several branches check git branch first. Setting git config --global init.defaultBranch main avoids needing it on new repositories.
Why does this happen in CI when it works locally?
Most CI systems check out a detached HEAD at a specific commit rather than a branch, so git branch --show-current returns nothing and there is no local main to push. Use git push origin HEAD:main, which pushes the current commit to the named remote branch without requiring a local branch. In GitHub Actions you can alternatively ask actions/checkout for a branch by passing ref.
What is a refspec?
The <source>:<destination> pair that tells Git what to push where. git push origin main is shorthand for main:main. The source is resolved locally, which is why this error is always about your own repository rather than the remote. Writing the source and destination separately is useful when they differ, as in git push origin HEAD:main, and an empty source as in git push origin :feature/old means delete the remote branch, which is why a typo in that form can be surprising.
How do I delete a remote branch?
git push origin --delete feature/old. The older equivalent, git push origin :feature/old, uses an empty refspec source to mean deletion and still works, but it is easy to mistype into something that pushes instead. The explicit --delete form says what it does. Deleting the remote branch does not remove your local copy, which you delete separately with git branch -d feature/old.