gitgit-refspec

Why does Git only store branches under refs/remotes/origin?


After looking at Git tags and seeing how they are propagated and how many workarounds there are to pruning them, I concluded that the best fix would be to change the structure of refs/remotes/origin/. So, why are refs under refs/remotes/origin/ assumed to be branches? It would have been easy to replicate the top-level structure under the remote ref too:

refs/remotes/origin/heads/  <-- remote tracking branches
refs/remotes/origin/tags/   <-- remote tracking tags
refs/remotes/origin/notes/  <-- remote tracking notes

But all tools that work with Git assume that refs under refs/remotes/origin/ are branches.

Is there an actual reason this is the way things are or is it merely an accident?

Edit 1:

After looking into this a little more I found that git log --decorate will correctly show annotated tags under the refs/remotes/origin/tags/* but lightweight tags show up as branches.

Config file:

[remote "origin"]
    url = ssh://git@github.com/test/example.git
    fetch = +refs/heads/*:refs/remotes/origin/heads/*
    fetch = +refs/tags/*:refs/remotes/origin/tags/*

Lightweight tag:

commit e447ca1e2f3c765072c6bd783981619da3d6a090 (tag: v0.2, origin/tags/v0.2)
Author: Joanna Blogs <joanna@blogs.com>
Date:   Thu Aug 18 14:38:48 2016 -0500

    Testing out a light weight tag

Annotated tag:

commit 334d587e8f9bad1756665384056760c0cb798f32 (tag: v0.1, tag: origin/tags/v0.1)
Author: Joe Blogs <joe@blogs.com>
Date:   Fri Jul 1 09:24:25 2016 -0500

    Testing an annotated tag

However, as expected, the git tag -l command doesn't show them at all.


Solution

  • [Edited substantially less than a day after being answered: previous answer was based on a different, mis-remembered issue.]

    Tags are intended to be global / universal. That is, there is no such thing, in Git, as a "remote tag".

    There is no technical reason Git can't have remote tags. In fact, it could have both remote-tracking-tags (always force-updated) and global tags (not-force-updated). Here's one way to implement them manually:

    [remote "R"]
        url = ...
        fetch = +refs/heads/*:refs/remotes/R/*
        fetch = +refs/tags/*:refs/rtags/R/*
        fetch = refs/tags/*:refs/tags/*
    

    Now when you git fetch R, if you already have a tag blue and they have an unrelated blue, you'd get refs/rtags/R/blue but would not update your own refs/tags/blue.

    (This is not all that convenient since you have to spell out rtags/R/blue, but in the case of a collision you would have to spell it out anyway to avoid ambiguity.)

    If you're asking: "remote tags instead of global tags seem like a good idea, why doesn't Git do them?", my answer would be "history, inertia, stubbornness, etc.", but of course you would really have to ask the Git maintainers directly. (It does seem impolite to remove global tags. :-) )

    If you're asking "remote tags in addition to global tags seems like a good idea, why doesn't Git do them?" my answer would be "I don't know". It's true that I had to put this under refs/rtags/R rather than refs/remotes/R/tags/ since there's no room in refs/remotes/R for anything other than branches. If the Git folks were to adopt this in full generality, perhaps your own implied suggestion, of simply copying refs/* to refs/remotes-new/*, then treating them as tags, notes, etc., automatically, would be best.