Ben C Ben C - 2 months ago 13
Git Question

Why does "git log --tags=<pattern>" add "refs/tags" to the output?

I am using

git log --source --tag=<pattern>
to limit the output to certain tags and display those tags. It qualifies the displayed tags with
refs/tags/
but it doesn't do this for
--tag
without a pattern (or if I don't specify
--tag
at all).

Why is this, and is it possible to turn it off?

$ git init
Initialised empty Git repository in /tmp/repo/.git/
$ touch new-file
$ git add new-file
$ git commit -m "Added new file new-file"
[master (root-commit) c151765] Added new file new-file
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 new-file
$ git tag TEST-TAG
$ git log --oneline --source --tags
c151765 TEST-TAG Added new file new-file
$ git log --oneline --source --tags=*
c151765 refs/tags/TEST-TAG Added new file new-file


(I have observed this on git 1.9.1 and 2.1.0.)

Answer

(Side note: --source adds the "source" reference from which some commit was first reached, to the git log output.)

[Sometimes the tags are fully-qualified.] Why is this, and is it possible to turn it off?

All of Git's references share one big name-space. Except for the special cases (HEAD, MERGE_HEAD, ORIG_HEAD, and so on) they all live under the top level directory1 refs/. The next level determines the type of the reference: refs/heads/ contains branch names, refs/remotes/ contains remote-tracking branches, and refs/tags/ contains tags. (There is also refs/notes/ for notes, and refs/stash, which is just a file rather than a directory, for git stash.)

When using --branches=<pattern>, --glob=<pattern>, --remotes=<pattern>, or --tags=<pattern>, you wind up seeing the full names. In a way, it's more interesting to see that this doesn't happen when using --tags (or --branches or --remotes; there's no equivalent for --glob). The reason, though, is that it's an implementation quirk. If you examine the linked code, you will see that the --tags= variant (at the bottom of the highlighted section) passes an explicit refs/tags/ to for_each_glob_ref_in, while the --tags variant (at the top) does not.

There are actually two source files with the same code pattern. The quirk is, I think, actually much clearer in builtin/rev-parse.c, but git log's annotations are due to the code in revision.c.

Since it is hard-coded in the source, the answer is no, you can't turn it off, you can only modify it after the fact.

Note that if you use --source --branches --tags, and you have branch and tag names that match2—such as refs/heads/blergh and refs/tags/blergh—and you see:

f0b2db2 blergh do something or other

you cannot tell if blergh is a branch name, or a tag name.


1When the references are packed, i.e., stored in .git/packed-refs, this "directory" notion is just conceptual, but originally, and still today when the references are unpacked, this really is a directory: if you peek into your .git directory, you will find refs/ which contains refs/heads/ and so on.

2This is a bad idea in general. It all works according to the rules, but the rules can be confusing. In particular, gitrevisions says the tag comes first, and it does with most Git commands (such as git show), but git checkout disagrees and will check out the branch, not the tag (which actually makes sense: to check out the tag, you can write refs/tags/blergh but doing git checkout refs/heads/blergh gets you a detached HEAD). If you find you have done this—made a branch and tag with the same name—by accident, un-do it. :-)