Chris Cleeland
Chris Cleeland

Reputation: 4900

Given a git refname, can I detect whether it's a hash, tag, or branch?

I'm writing a script that will take in a specification used as the base for creating a branch. Thus, it will have something like this:

git checkout -b $newbranch $startingpoint

Now, startingpoint can be specified either as a branch name, a tag, or a SHA1.

Later in the script, I need to do different things based on what startingpoint actually is. Is had thought git rev-parse would give me this information, but I cannot seem to bend it to my whim.

Is there a git way, preferably a porcelain, to get the information I seek?

Update

I used the show-ref information to craft the following shell function:

git_ref_type() {
    [ -n "$1" ] || die "Missing ref name"

    if git show-ref -q --verify "refs/heads/$1" 2>/dev/null; then
        echo "branch"
    elif git show-ref -q --verify "refs/tags/$1" 2>/dev/null; then
        echo "tag"
    elif git show-ref -q --verify "refs/remote/$1" 2>/dev/null; then
        echo "remote"
    elif git rev-parse --verify "$1^{commit}" >/dev/null 2>&1; then
        echo "hash"
    else
        echo "unknown"
    fi
    return 0
}

This seems to work well for now, though it's been only very lightly tested.

Upvotes: 52

Views: 8957

Answers (2)

VonC
VonC

Reputation: 1323263

Note that git 1.8.5 or 1.9 (Q4 2013) will provide for a new way to test if a ref is a tag or not.

Make "foo^{tag}" to peel a tag to itself, i.e. no-op., and fail if "foo" is not a tag.

How to test a revision is a tag? (or "check if a git name is a tag"?)

git rev-parse --verify v1.0^{tag}

More convenient than: test $(git cat-file -t v1.0) = tag".

See commits 75aa26d and abdb54a1d, by Richard Hansen:

peel_onion(): add support for <rev>^{tag}

Complete the <rev>^{<type>} family of object descriptors by having <rev>^{tag} dereference <rev> until a tag object is found (or fail if unable).

At first glance this may not seem very useful, as commits, trees, and blobs cannot be peeled to a tag, and a tag would just peel to itself.
However, this can be used to ensure that <rev> names a tag object:

$ git rev-parse --verify v1.8.4^{tag}
04f013d
$ git rev-parse --verify master^{tag}
error: master^{tag}: expected tag type, but the object dereferences to tree type
fatal: Needed a single revision

Users can already ensure that <rev> is a tag object by checking the output of 'git cat-file -t <rev>', but:

  • users may expect <rev>^{tag} to exist given that <rev>^{commit}, <rev>^{tree}, and <rev>^{blob} all exist
  • this syntax is more convenient/natural in some circumstances

However, as mentioned in the comments by mtraceur, that would not work for a lightweight tag (see "creating tags"), which is just name a for an object (usually a commit object).

You would get an error like:

Eexpected tag type, but the object dereferences to tree type

Because, with a lightweight tag, would would be actually parsing a commit, instead of an actual tag.
From "Git Internals - Git References":

https://git-scm.com/book/en/v2/images/data-model-4.png

If you make a lightweight tag on a commit, git rev-parse ^{tag} would actually parsing and dereferencing the commit itself, not the tag.

Upvotes: 10

larsks
larsks

Reputation: 311308

You can probably use the show-ref command to do (mostly) what you want. Given some string, if it refers to a tag then...

git show-ref --verify refs/tags/$thestring

...will be true. If it's a branch name, then...

git show-ref --verify refs/heads/$thestring

...will be true. If the string fails both of those tests, then...

git rev-parse --verify "$thestring^{commit}"

...will tell you if it otherwise refers to a commit (which could be a complete SHA1, a partial SHA1, HEAD, or possibly something else that doesn't fall into the previous two categories.

Upvotes: 32

Related Questions