Reputation: 1379
I checkout a particular commit (and hence, end up in a detached HEAD state) and then make a few commits on top of it. Let's say I did this in repository A. Let's further assume I have an another repository B which has A as one of its remotes. Now, in B, how do I fetch the detached HEAD of A without creating a new branch in A?
Upvotes: 3
Views: 1464
Reputation: 487755
As ElpieKay answered in a comment, use git fetch remote HEAD
, which saves the fetched commit's hash ID in the special FETCH_HEAD
file. You can then use FETCH_HEAD
as a reference until the next git fetch
overwrites it.
Both fetch and push operations work with names, but they are not symmetric.
They are symmetric when it comes to transferring commits. That is, whether you run git fetch remote [refspec...]
or git push remote [refspec...]
, the sending and receiving Git systems have a conversation involving object hash IDs, where the sender advertises which hash IDs the sender would like to give to the receiver: I have <hash> for you, and the receiver sends back replies saying that the sender should send that, or—if the receiver already has that object—not send it. (It's a little more complicated than this, since a fetch-receiver kicks off the process with the first "wants", but close enough.)
When this is done, though, the push
operation has the sender send over a series of recommended <refname, hash-ID> pairs: Please set your refs/heads/master
to a123456...
for instance. This means that if you are doing git push
whlie you are on a detached HEAD
in your repository, you still have to give the other Git a name for this commit:
git push origin HEAD:refs/heads/somebranch
for instance suffices to have your Git send the hash ID for your HEAD
commit, but recommend that their Git set their refs/heads/somebranch
to that hash ID when done. You cannot ask them to set their HEAD
: if you try, they'll just create a branch named HEAD
, i.e., refs/head/HEAD
, if you're on a branch now, or reject your push request if not:
error: unable to push to unqualified destination: HEAD
On the other hand, when you run git fetch
, you control which reference(s), if any, get updated on your end. Their Git simply sends a list of all their references (in protocol v0 anyway; v2 is fancier). Your Git picks over the list and, if they sent you new hash IDs for their refs/heads/master
and refs/heads/branch
, your Git will generally update your own refs/remotes/origin/master
and refs/remotes/origin/branch
. Your Git takes the list of their references, generates your side's "want" list of hash IDs, and delivers that to the sender to kick off the have/want hash ID conversation.
That is, that's what your Git does if you run git fetch origin
, with no added refspec
arguments, and assuming your configuration is normal (not the special configuration left behind for a --single-branch
clone for instance). But if you do add refspec arguments, e.g.:
git fetch origin refs/heads/master:refs/weird/name
then your Git asks their Git to send only the commits you need to work with their master
. That is, the have/want conversation starts with only the hash ID in their refs/heads/master
(and even then, only if you don't already have it). When the have/want is done and the objects have arrived in your repository, your Git then creates or updates your refs/weird/name
reference.
Remember, these refspecs have the general form src:*dst
. The src
part is the source reference—the name or hash ID that the sender uses to find the commit—and the dst
part is the destination reference that the receiver should use to remember the hash ID in the end. You can omit one of the two by writing src
or :dst
, which has various special-case meanings depending on push vs fetch. Whether a raw hash ID works in the src
part of this expression depends on two things:
push
, it always works (as long as the object exists);fetch
, it works if and only if they allow it.(So here, we already see that fetch and push are asymmetric.)
For git fetch
, if you omit the :dst
part of the refspec—e.g., git fetch origin refs/heads/master
or git fetch origin master
—your Git skips the create-or-update part, except for so-called opportunistic updates (creating or updating refs/remotes/origin/master
, in this case). However, for each name that your git fetch
obtained, your Git always writes that <name, hash-ID> pair to your FETCH_HEAD
file:
$ git fetch origin HEAD master
From ...
* branch HEAD -> FETCH_HEAD
* branch master -> FETCH_HEAD
$ cat .git/FETCH_HEAD
f84b9b09d40408cf91bbc500d9f190a7866c3e0f <url>
f84b9b09d40408cf91bbc500d9f190a7866c3e0f branch 'master' of <url>
(Note that although git fetch
got many branches and tags in the list of name/ID pairs from origin
, we only asked for HEAD
and master
, so that's what git fetch
wrote into .git/FETCH_HEAD
.)
If you're sending commits, you must provide a name for the other Git. Usually the name is implied: you push your branch bran
, so the name you want them to set is their branch bran
. You can push any object: it is up to their Git, after receiving the object, to decide whether to accept the <name, hash-ID> pairing. Usually, you'll push a commit object, which will drag with it all the other objects required, and you'll have them set a branch name.
If you're receiving commits, though, you need not provide a name on your side. Their Git will send their names and objects, and your Git will use your .git/FETCH_HEAD
file to remember the hash IDs you got from them. If you do provide names on your side, your Git will update those names, and if you don't, Git has some complicated default rules for fetching, to remember their branch names via refs/remotes/remote/
names.
While HEAD
is not itself a branch name, it is a valid name. You may not be able to make them update their detached HEAD
(via push
), but you normally can have them send you the commit hash stored in their detached HEAD
, which your Git will remember as "unnamed" in your .git/FETCH_HEAD
.
Upvotes: 4
Reputation: 11
I think you can't just push a detached head. If you want to see those changes in B, you have to push the commits somewhere in A. Either into a new branch or into an existing one. Since you don't want to create a new branch in A, I'm assuming that you have pushed the commits into the original branch. Therefore, you can access them with a simple pull.
If you haven't pushed the commits anywhere, I would say that the best approach is to make the changes directly in repository B, if that is an option.
Upvotes: 0