9

I am looking for a Git command that will print the symbolic name for HEAD. I was using this command

$ git name-rev --name-only HEAD master $ git checkout HEAD~2 $ git name-rev --name-only HEAD master~2 

However I noticed that it does not work on a new repository

$ git init $ git name-rev --name-only HEAD Could not get sha1 for HEAD. Skipping. 

I found this workaround

$ git init $ git symbolic-ref --short HEAD master 

But this does not work on older commits

$ git checkout HEAD~2 $ git symbolic-ref --short HEAD fatal: ref HEAD is not a symbolic ref 

So now I have this, which covers both new repos and older commits

$ git symbolic-ref -q --short HEAD || git name-rev --name-only HEAD master 

I was just wondering, is this the best way to do this? It feels like this should be possible with one command.

2
  • 5
    I'm not sure your problem is well posed. In case your HEAD is detached, git name-rev --name-only HEAD only gives you a "relative" symbolic reference (for lack of a better term). Strictly speaking, in detached HEAD state (like after your 2nd command in your first code block), there is no symbolic name unambiguously associated with HEAD. Commented Dec 23, 2014 at 10:24
  • Can you please add your expected output (commit ID, tag name if present, branch name, all branch names if applicable)? Any why do you expect commits and refs to be present in a pristine repo? Commented Dec 25, 2014 at 22:59

2 Answers 2

14
+50

You write:

I am looking for a Git command that will print the symbolic name for HEAD.

The following may suffice to demonstrate that what you're asking for does not make sense in all situations, simply because HEAD may not be unambiguously associated with one reference. Read on.

What git name-rev does

In the git-name-rev man-page, you find the following description:

Given a commit, find out where it is relative to the local refs. [...]

More specifically, git name-rev checks whether <commit-ish> is reachable from any of the local references (by processing them in lexicographical order, e.g. develop before master).

Let's examine what

$ git name-rev --name-only <commit-ish> 

does in different cases (the --name-only flag is incidental here, as its effects are purely cosmetic).

Case in which HEAD is not detached

If HEAD is not detached, i.o.w. if HEAD is pointing to a valid ref (let's call it myref), then there is no ambiguity: running

$ git name-rev --name-only HEAD 

simply outputs

myref 

because the myref reference is unambiguously associated with HEAD. So far, so good.

Case in which HEAD is detached

In that case, things are not as simple. In fact, there may be one or more references from which <commit-ish> is reachable, or there may be none at all.

Case in which there are one or more such local references

At the first such local reference found, git name-rev prints a "relative" symbolic reference, i.e. a revision of the form

<ref>~<n> 

where <ref> stands for the local reference in question, and <n> stands for the generation. For example, if HEAD points directly to a commit that is a grandparent of master (master being the only local reference), then

$ git name-rev HEAD 

returns

master~2 

Note, however, that in case <commit-ish> is reachable from multiple references, the one returned by git name-rev is somewhat arbitrary, as it's only dictated by the lexicographical order (in which the command checks local references).

Case in which there is no such local reference

It's easy to imagine situations in which <commit-ish> is reachable from none of the local references. Actually, here is one you can reproduce at home (boilerplate stdout is omitted):

# set things up $ mkdir test $ cd test $ git init # create a commit $ touch README.md $ git add README.md $ git commit -m "add README" # detach the HEAD (make it point directly to the tip of master, instead of to master itself) $ git checkout $(git rev-parse master) # create a second commit (while in detached-HEAD state) $ printf "foo\n" > README.md $ git commit -am "write 'foo' in README" # attempt to find a symbolic name for HEAD $ git name-rev --name-only HEAD undefined 

Because the commit DAG looks as follows,

A [master] \ B [HEAD] 

commit B is not reachable from the only reference (master); therefore, git name-rev gives up and simply returns undefined.

Conclusion

Because HEAD is not guaranteed to be unambiguously associated to one reference, what you're asking for doesn't make sense :p

Sign up to request clarification or add additional context in comments.

Comments

5

Just want to add my solution for your case:

git symbolic-ref -q --short HEAD || git describe --all --always HEAD 

This covers branches, tags, detached heads (commits) and new repositories. But tags will be returned as tags/0.1.0 for example.

2 Comments

This command is fine if a branch is checked out. However, it won't work in detached-HEAD state, because asking for a symbolic name associated with HEAD when the latter is detached simply doesn't make sense.
The detached-HEAD state is handled by the || alternate. Another way of saying like this: git symbolic-ref --short HEAD -q || echo "(detached HEAD)"

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.