Dmitry Petrov
Dmitry Petrov

Reputation: 1547

Pushing Git non-branch references to a remote

Git non-branch references (not branches, tags, remotes and notes) work just fine in a local machine but I have trubles to push them in a remote:

$ git update-ref refs/exp/ee01 6a534fb5f9aad615ebeeb9d01ebe558a679a3cd1

It was successfully created:

$ cat .git/refs/exp/ee01
6a534fb5f9aad615ebeeb9d01ebe558a679a3cd1
$ git for-each-ref refs/exp
6a534fb5f9aad615ebeeb9d01ebe558a679a3cd1 commit refs/exp/ee01

Pushing it:

$ git push origin exp/ee01
Total 0 (delta 0), reused 0 (delta 0)
To https://github.com/dmpetrov/example-get-started-exp.git
 * [new branch]      refs/exp/ee01 -> refs/exp/ee01

However, I don't see it when I clone this repo:

$ git clone https://github.com/dmpetrov/example-get-started-exp.git
$ cd example-get-started-exp/
$ git for-each-ref refs/exp  # it returns nothing

How to push non-branch references properly?

EDIT: I can fetch it by name to FETCH_HEAD. Ideally, I should see\fetch all the new refs without knowing the names in advance.

$ git fetch origin exp/ee01
From https://github.com/dmpetrov/example-get-started-exp
 * branch            refs/exp/ee01 -> FETCH_HEAD

Upvotes: 6

Views: 542

Answers (2)

torek
torek

Reputation: 489838

Refspecs as a general concept are great, but there's a somewhat unfinished feeling to them. 😀

When you first clone some existing repository, your git clone uses a built-in equivalent to git remote add to add the remote name. As the git remote documentation notes (a bit elliptically - see meaning 2a):

With -t <branch> option, instead of the default glob refspec for the remote to track all branches under the refs/remotes/<name>/ namespace, a refspec to track only <branch> is created. You can give more than one -t <branch> to track multiple branches without grabbing all branches.

What this boils down to is the fact that after git clone, the (single) default fetch refspec for the new clone is:

+refs/heads/*:refs/remotes/<name>/*

where <name> is the name from the -o option, or origin if you did not specify such an option.1

What it doesn't mention explicitly, and is not obvious, is that the remote.remote.fetch setting in a Git configuration file is cumulative.2 This means that you can open up the existing .git/config file, once git clone has created it, and edit it. You will see:

[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*

You can change this to add another line, so that it reads:

[remote "origin"]
    fetch = +refs/heads/*:refs/remotes/origin/*
    fetch = +refs/exp/*:refs/exp/*

Now any git fetch origin will overwrite any of your existing refs/exp/ references with those that are on origin. Fetching with prune = true or with the -p or --prune option will delete any of your existing refs/exp/* references that have no corresponding name on origin.

If you wish to replace their refs/exp/* names with your own refs/rexp/origin/* names, make the second line read:

    fetch = +refs/exp/*:refs/rexp/origin/*

and now you have invented exp-tracking names.

(Given that there is no refs/tags/*:refs/tags/* refspec—with or without a leading + sign—you might wonder how tags work at all. The answer here is "somewhat magically, with internal rules that cannot be expressed through a refspec". That's part of what I mean about the somewhat unfinished feeling. It's also not obvious what to put in during a git clone, but note that git clone -c name=value lets you write configuration values at git clone time. You still need to somehow know that the remote you're cloning has refs/exp/* names, though.)


1In a forthcoming Git release, the -o option is likely to have a configurable default, so that leaving out -o won't necessarily mean use origin, but for now, that's what it always means.

2In contrast, a setting such as user.name or user.email uses only the last value. That is, if your configuration file says:

[user]
    name = fred
    name = flintstone

then user.name is flintstone: the earlier fred value has been discarded in favor of the later flintstone one. A cumulative setting can only obtained with git config --get-all or git config --get-regexp; it comes out as one line per value. See the git config documentation for more details.

Upvotes: 7

eftshift0
eftshift0

Reputation: 30307

In my experience, if you want to push some random thing into a remote into a new (in the remote) branch, then you have to first push an existing branch from your local and then you can push the id you want:

git push origin master:new-branch
git push origin the-id-i-really-want:new-branch

Upvotes: 0

Related Questions