Search code examples
gitgithubcontinuous-integrationtagsbranch

git push error: dst refspec refs/heads/main matches more than one


At the moment I'm forced to push to a new branch on every change I make. And that branch must be deleted since the error appears again on every newly created branch.

git push origin main gives the following output

error: dst refspec refs/heads/main matches more than one
error: failed to push some refs to 'https://github.com/CarloCattano/fastesTube'

My git tag outputs:

refs/heads/dev
refs/heads/dev2
refs/heads/dev3
refs/heads/main
v1.1
win64 

no matter if I manually delete them with -d. Even tried to migrate the project to a new repo , and the problem persists after a few pushes.

git remote -v

origin  https://github.com/CarloCattano/fastesTube (fetch)
origin  https://github.com/CarloCattano/fastesTube (push)

git ls-remote

ac4cac50b79ff682ddd01f6c0c3913d0bd765e64        HEAD
77273d612953f96e72ce305ab94f0a535a4c332d        refs/heads/dev3
3c344e7af2feb33db2d05f08866cad5fe624b57c        refs/heads/develop
ac4cac50b79ff682ddd01f6c0c3913d0bd765e64        refs/heads/main
fde3bb1ed7c770a5b8eb94a368bb34f25566f00e        refs/pull/1/head
ffe33059c3fcc12899953bc588772072d9a18bf0        refs/pull/2/head
77273d612953f96e72ce305ab94f0a535a4c332d        refs/pull/3/head
3c344e7af2feb33db2d05f08866cad5fe624b57c        refs/pull/4/head
b9d1c3f8b83ea1ac868143ec647776d03f9bacc7        refs/tags/refs/heads/dev
ffe33059c3fcc12899953bc588772072d9a18bf0        refs/tags/refs/heads/dev2
77273d612953f96e72ce305ab94f0a535a4c332d        refs/tags/refs/heads/dev3
4098ea71b5a0873db6be41e859e5b8242d81c708        refs/tags/refs/heads/main
a42341ba40635bd5063a0bf988eab6c00b0e62d1        refs/tags/v1.1
37220afec1d13dcac99c61ef766ac800fc6438f5        refs/tags/win64

Force pushing also doesn't seem to work.

It might be I wrongly configured the .yml file and creates tags for every release.


Solution

  • The problem here is that you have created tags named refs/heads/... (fill in the three dots).

    A ref or reference, in Git, is a string that normally starts with refs/ and goes on to have a namespace qualifier:

    • refs/heads/* are branch names: the part that matches the * is the branch name;
    • refs/tags/* are tag names: the part that matches the * is the tag name;
    • refs/remotes/* are remote-tracking names;

    and so on. Typically you can give Git a shortened name, such as main or v1.1, and it can figure out whether this name is a branch name or a tag name by looking at the existing names:

    • There's normally a refs/heads/main or refs/heads/master, but no refs/tags/main or refs/tags/master, so main or master is therefore a branch name.
    • There may be a refs/tags/v1.1, but typically there isn't a refs/heads/v1.1 if so, so v1.1 is therefore a tag name.

    When using this kind of scheme, you provide an ambiguous name like main or v1.1 and Git figures out if it's a branch or tag name on its own, or you provide a full name like refs/heads/main and Git immediately knows that it's a branch name. The short name, without the refs/heads/ or refs/tags/ qualifier in front, is an unqualified name. The full name is a qualified name.

    The git push command is more complicated than most other Git commands (except that git fetch is similarly complicated) because it has to deal with two Git repositories, rather than just one. So instead of a ref, git push can take a refspec, which is a pair of refs separated by a colon :. If you use a full refspec:

    git push origin refs/heads/main:refs/tags/v1.2
    

    then the parts on the left and right are each refs and each one is either unqualified or qualified. Unqualified names can be resolved by looking at the local Git repository's names (for those that are local) or remote's name (for those that are remote).

    If, however, you use a partial refspec:

    git push origin main
    

    then Git isn't sure whether you meant "main as found locally" or "main as found on the remote". So Git will look in both places to make its best guesses.

    In this case, however, the destination Git—the one whose references are shown in your git ls-remote output—has both refs/heads/main and refs/tags/refs/heads/main. So, your Git has looked up main locally and found refs/heads/main; it now looks that up in the set of refs in the other Git repository, and can't translate it to just one ref since both refs/heads/main—a fully-qualified branch name—and refs/tags/refs/heads/main—a fully-qualified tag name—match the potentially-unqualified refs/heads/main. The result is that you get this error message.

    Whether you'd get this error for git push origin refs/heads/main:refs/heads/main, I don't know. The best fix, though, is to correct the set of names on the destination, so that there are no refs/tags/refs/* names any more. That is, these four names:

    b9d1c3f8b83ea1ac868143ec647776d03f9bacc7        refs/tags/refs/heads/dev
    ffe33059c3fcc12899953bc588772072d9a18bf0        refs/tags/refs/heads/dev2
    77273d612953f96e72ce305ab94f0a535a4c332d        refs/tags/refs/heads/dev3
    4098ea71b5a0873db6be41e859e5b8242d81c708        refs/tags/refs/heads/main
    

    should be adjusted (or deleted entirely) so that no tag name in the Git repository over on GitHub starts with refs/. Ideally, these tag names should not match any branch names either: tag names should generally conform to the v* form, or to some other "clearly a tag" pattern so that nobody will accidentally think they're branch names, or vice versa.

    Once you fix that, simple git push commands will work again.