Recently I had a scenario where I was in a detached HEAD state. I wanted to push this to a fork on github in order to share some work-in-progress code with a teammate. I had no need for a local branch name for this particular commit.
Clearly, this would not work:
git push sandy-github HEAD
That makes sense, as I wasn't specifying a name for the remote branch.
But I don't understand why this did not work:
git push sandy-github HEAD:mynewbranch
This resulted in the following error:
error: unable to push to unqualified destination: mynewbranch The destination refspec neither matches an existing ref on the remote nor begins with refs/, and we are unable to guess a prefix based on the source ref. error: failed to push some refs to 'git@github.com:sandyarmstrong/myreponame.git'
I ended up having to do:
git push sandy-github HEAD:refs/heads/mynewbranch
This worked. From the docs:
git push origin master:refs/heads/experimental
Create the branch experimental in the origin repository by copying the current master branch. This form is only needed to create a new branch or tag in the remote repository when the local name and the remote name are different; otherwise, the ref name on its own will work.
I just don't understand why this was necessary. I'm guessing there's something important about git I'm misunderstanding here. Why is this trickier syntax necessary just because the names don't match? Why isn't the HEAD:mynewbranch
syntax sufficient to let git know that it should generate a new branch on the remote named "mynewbranch"?
The refspec
part of the git push
documentation has this (emphasis mine):
The
<dst>
tells which ref on the remote side is updated with this push. Arbitrary expressions cannot be used here, an actual ref must be named. If:<dst>
is omitted, the same ref as<src>
will be updated.
In the normal state, git can determine that you meant to push to a certain branch (or tag) based on branch tracking and/or values of push.default
in config.
In the detached HEAD state, git cannot guess whether you want to create a new branch or a new tag (both could be reasonable here).
It's possible to imply that the branch be created by creating the local branch first:
git checkout -b mynewbranch
git push -u sandy-github mynewbranch
If you don't want to checkout the branch you're pushing, you can use the refs/heads/
prefix as you mentioned in your question:
git push sandy-github HEAD:refs/heads/mynewbranch
Git has a fair amount of "magic" or "DWIM"* in many commands. "Push" has this: if you push local branch b
to the remote and the remote has a b
, it uses the magic/DWIM part to reconcile the local ref (whose "real" name is refs/heads/b
) with the remote one (whose name, on the remote, is also refs/heads/b
). But you can also push refs/tags/t
, or now that notes exist, refs/notes/commits
.
It already also has magic/DWIM to realize that a local ref y
that resolves to the local full-name refs/x/y
should create the same refs/x/y
on the remote when you use y:y
, even if y
does not exist on the remote. It's just that if the local full-name does not resolve to something like this, that this fails. It can even resolve HEAD when it is the symbolic name of a branch, but when HEAD is "detached", there is no symbolic name.
It could assume that git push HEAD:foo
"means" to create refs/heads/foo
. That's probably the most likely, hence what you meant. But, it does not (yet?).
*DWIM: "Do What I Mean" (as opposed to what I say).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With