git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: Strange behaviour when pushing a commit object to remote's refs/HEAD

From
Karthik Nayak <karthik.188@gmail.com>
Date
Jan 16, 2024, 13:24 UTC
Message-ID
<CAOLa=ZRfr+oEKCo8AfFSAFtS8pbDgmG_EeBSwm7GwukzVcqSrg@mail.gmail.com>
In-Reply-To
<mafs0a5p5pl6y.fsf@yadavpratyush.com>
Pratyush Yadav <me@yadavpratyush.com> writes:
Show 9 quoted lines
>> Just to note here that pushing to "refs/heads/HEAD" is not actually
>> updating the remote repositories $GIT_DIR/HEAD file, rather it creates a
>> new reference $GIT_DIR/refs/heads/HEAD.
>
> Yes, that is what I would also expect. I checked one of the Git servers
> we have and this is exactly what happens. $GIT_DIR/HEAD is a symref
> pointing to refs/heads/main and $GIT_DIR/refs/heads/HEAD points to the
> commit. But behaviour from client side is not consistent.
>
What is the non _consistent_ part?
Show 9 quoted lines
>>
>> With this understanding you'll see that this is not a bug, because the
>> remote HEAD was never updated, but only a new branch called HEAD was
>> created [0].
>
> GitHub thinks so but try opening the branch. It won't show you the
> commit (707a3d5, "2.1") but instead shows you 86e1c97 ("2"). So
> something is wrong _at least_ with Github.
>

I don't know how GitHub operates, but I'm guessing because there is ambiguity between a branch called HEAD and the actual HEAD. So this is probably the reason.

Show 30 quoted lines
>>
>>> Now with such a repo, if you do `git log --all --oneline` it would look
>>> something like:
>>>
>>>     707a3d5 (origin/HEAD) 2.1
>>>     86e1c97 (HEAD -> main, origin/main) 2
>>>     79264c3 1
>>>
>>> And running `git for-each-ref --format='%(symref:short),%(refname:short),%(refname),%(subject)' refs/remotes/origin` gives:
>>>
>>>     ,origin,refs/remotes/origin/HEAD,2.1
>>>     ,origin/main,refs/remotes/origin/main,2
>>>
>>> All well and good so far. Now delete the repo and attempt to clone it.
>>> This time `git log --all --oneline` gives:
>>>
>>>     86e1c97 (HEAD -> main, origin/main, origin/HEAD) 2
>>>     79264c3 1
>>>
>>
>> This is expected since you cloned the repository and you got the default
>> branch 'main'.
>
> No.
>
> First, if I clone a repo with multiple branches (say
> https://github.com/prati0100/git-gui) I get _all_ the remote branches.
> Yet here I clearly don't get the so called "HEAD" branch. This is not
> expected behaviour.
>

You're right, I meant to say that the remote branches don't have the corresponding local branches. But that does not matter here.

I'm not saying that there is a path for git to work properly when creating a branch called "HEAD". It's just that "HEAD" is more of a reserved word for git and creating a branch with the same name has unintended effects.

Show 14 quoted lines
> Second, git really does misunderstand refs/remotes/origin/HEAD. For
> example, when running git for-each-ref command with the clone method, I
> get:
>
>     origin/main,origin,refs/remotes/origin/HEAD,2
>
> So it clearly thinks refs/remotes/origin/HEAD is at 86e1c97 ("2"). Or,
> to be more specific, it thinks the ref points to origin/main which is at
> 86e1c97 ("2"). But we set it at (707a3d5, "2.1"). So it tells me the
> wrong thing. Now if I do the git remote add && git remote update method,
> git for-each-ref says:
>
>     ,origin,refs/remotes/origin/HEAD,2.1
>
This is one of those ambiguities, we store HEAD for remotes as
     $GIT_DIR/refs/remotes/<remote>/HEAD
and remote branches as
     $GIT_DIR/refs/remotes/<remote>/<branch>

So what happens if there is a branch named HEAD? This is the problem you're facing...

Show 26 quoted lines
> So now it thinks refs/remotes/origin/HEAD points at (707a3d5, "2.1"). I
> do not see it as expected behaviour.
>
> We can also see this when inspecting the contents of
> .git/refs/remotes/origin/HEAD. With clone it says:
>
>     ref: refs/remotes/origin/main
>
> With git remote add && git remote update it says:
>
>     707a3d587c61c089710e3924eb63a51763b5a4c8
>
> The same ref points to different places based on how you pull the repo.
>
> Looking deeper, if you clone a repo that does not have a branch called
> "HEAD" (like git-gui), git creates a file in
> .git/refs/remotes/origin/HEAD that says:
>
>     ref: refs/remotes/origin/master
>
> So it certainly seems to use refs/remotes/origin/HEAD to point to the
> remote's HEAD, and not as a regular branch.
>
> I find this to be inconsistent behaviour on git's part and do not think
> it is (or should be) expected behaviour.
>

Maybe we should explicitly mention that using HEAD as the branch name has unintended effects and should be avoided.

Show 17 quoted lines
>>
>>> And running `git for-each-ref --format='%(symref:short),%(refname:short),%(refname),%(subject)' refs/remotes/origin` gives:
>>>
>>>     origin/main,origin,refs/remotes/origin/HEAD,2
>>>     ,origin/main,refs/remotes/origin/main,2
>>>
>>> So suddenly the remote's HEAD becomes origin/main (symbolic ref) and the
>>> commit (707a3d5, "2.1") is nowhere to be found. It neither shows up in
>>> `git rev-list --all` nor in `git log --all`. The files and trees
>>> associated with it also do not show up in `git rev-list --all --object`.
>>
>>
>> Because rev-list's `--all`, iterates over all refs. Since you only
>> cloned, the HEAD branch is not pulled.
>
> Why not? When you clone all branches should get pulled.
>

I think I jumped too quick here, it is because the branch HEAD is never realized locally as I explained above.

Previous: Pratyush YadavNext: Jeff King
Message 4 of 5 in “Strange behaviour when pushing a commit object to remote's refs/HEAD”
  1. Pratyush YadavJan 15, 2024
  2. Karthik NayakJan 16, 2024
  3. Pratyush YadavJan 16, 2024
  4. Karthik NayakJan 16, 2024
  5. Jeff KingJan 16, 2024

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.