Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the git push syntax for creating a new remote branch from a detached HEAD so different?

Tags:

git

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 '[email protected]: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"?

like image 552
Sandy Avatar asked Sep 16 '13 23:09

Sandy


2 Answers

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
like image 68
cmbuckley Avatar answered Oct 08 '22 17:10

cmbuckley


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).

like image 24
torek Avatar answered Oct 08 '22 18:10

torek