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
Pratyush Yadav <me@yadavpratyush.com>
Date
Jan 16, 2024, 11:33 UTC
Message-ID
<mafs0a5p5pl6y.fsf@yadavpratyush.com>
In-Reply-To
<CAOLa=ZS8YBhzaYx=9016KxErsMsazsF09rcuPs=-WpEGjV+ruw@mail.gmail.com>
On Tue, Jan 16 2024, Karthik Nayak wrote:
Show 29 quoted lines
> Pratyush Yadav <me@yadavpratyush.com> writes:
>
>> Hi,
>>
>
> Hello,
>
>> I ran into a strange Magit bug, where when I ran magit-show-refs on a
>> particular repo it threw an error. The details of the Magit bug are not
>> very interesting, but when attempting to reproduce it, I also saw git
>> misbehaving for such repos.
>>
>> The strange behaviour happens when you push a commit object to remote's
>> refs/HEAD instead of pushing a symbolic ref. Such a repository can be
>> found at https://github.com/prati0100/magit-reproducer. I roughly used
>> the below steps to create such a repo:
>>
>>     $ git init
>>     $ echo 1 > foo && git add foo && git commit
>>     $ echo 2 > bar && git add bar && git commit
>>     $ git push
>>     $ git checkout 79264c3
>>     $ echo 2.1 > bar && git add bar && git commit
>>     $ git push origin 707a3d5:refs/heads/HEAD
>>
>
> 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.

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

Show 22 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.

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

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.

Show 14 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.
Show 5 quoted lines
>
> Everything else is a consequence of the subtle but important difference
> between updating $GIT_DIR/HEAD vs creating $GIT_DIR/refs/heads/HEAD.
>
> [0]: https://github.com/prati0100/magit-reproducer/branches/all
-- 
Regards,
Pratyush Yadav
Previous: Karthik NayakNext: Karthik Nayak
Message 3 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.