74

I am creating a deploy script for a zend application. The script is almost done only I want to verify that a tag exists within the repo to force tags on the team. Currently I have the following code:

# First update the repo to make sure all the tags are in
cd /git/repo/path
git pull

# Check if the tag exists in the rev-list. 
# If it exists output should be zero, 
# else an error will be shown which will go to the else statement.
if [ -z "'cd /git/repo/path && git rev-list $1..'" ]; then
    
    echo "gogo"

else

    echo "No or no correct GIT tag found"    
    exit

fi

Looking forward to your feedback!

Update

When I execute the following in the command line:

cd /git/repo/path && git rev-list v1.4..

I get NO output, which is good. Though when I execute:

cd /git/repo/path && git rev-list **BLA**..

I get an error, which again is good:

fatal: ambiguous argument 'BLA..': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions

The -z in the statement says, if sting is empty then... In other words, it works fine via command line. Though when I use the same command in a shell script inside a statement it does not seem to work.

[ -z "'cd /git/repo/path && git rev-list $1..'" ]

This method is inspired by Validate if commit exists

Update 2

I found the problem:

See Using if elif fi in shell scripts >

sh is interpreting the && as a shell operator. Change it to -a, that’s [’s conjunction operator:

[ "$arg1" = "$arg2" -a "$arg1" != "$arg3" ] Also, you should always quote the variables, because [ gets confused when you leave off arguments.

in other words, I changed the && to ; and simplified the condition. Now it works beautiful.

if cd /path/to/repo ; git rev-list $1.. >/dev/null

then

    echo "gogo"

else
    echo "WRONG"
    exit
fi
MrBens
  • 1,227
  • 1
  • 9
  • 19
Kim
  • 977
  • 1
  • 8
  • 15
  • is the `$sha` variable defined elsewhere? – CharlesB Jul 22 '13 at 14:35
  • also note that `git pull` does a merge after updating the references, you'd want to use `git fetch` instead – CharlesB Jul 22 '13 at 14:35
  • and see [Bash/Shell Script Function to Verify Git Tag or Commit Exists and Has Been Pushed to Remote Repository](http://stackoverflow.com/q/3418674) – CharlesB Jul 22 '13 at 14:37
  • Oke let me explain a bit more, see the update in the description. – Kim Jul 22 '13 at 14:56
  • I would suggest that `git rev-parse --verify refs/tags/${tagname}` is probably the better way to do this... (It at least seems to be the primary `git` does this internally...). – twalberg Jul 22 '13 at 15:17
  • "'cd /git/repo/path && git rev-list $1..'" isn't an empty string - it contains 'cd /git/repo/path && git rev-list $1..' - perhaps you meant to execute the command rather than just putting it as text? – Douglas Leeder Jul 22 '13 at 16:02

7 Answers7

111

Why so complicated? Here’s a dead-simple solution (based on cad106uk’s approach further down the page):

version=1.2.3

if [ $(git tag -l "$version") ]; then
    echo yes
else
    echo no
fi

It is not necessary to compare the output of git tag -l with the version number, because the output will be empty if the version is not found. Therefore it’s sufficient to test if there’s any output at all.

Note: The quotes around $version are important to avoid false positives. Because if $version is empty for some reason, git tag -l would just list all tags, and the condition would always be true.

lxg
  • 12,375
  • 12
  • 51
  • 73
59

You could use git rev-parse instead:

if GIT_DIR=/path/to/repo/.git git rev-parse $1 >/dev/null 2>&1
then
    echo "Found tag"
else
    echo "Tag not found"
fi

git rev-list invokes graph walking, where git rev-parse would avoid it. The above has some issues with possibly looking up an object instead of a tag. You can avoid that by using ^{tag} following the tag name, but this only works for annotated tags and not lightweight tags:

if GIT_DIR=/path/to/repo/.git git rev-parse "$1^{tag}" >/dev/null 2>&1
then
    echo "Found tag"
else
    echo "Tag not found"
fi

@Lassi also points out that if your tag name begins with a -, then it might get interpreted as an option instead. You can avoid that issue by looking for refs/tags/$1 instead. So in summary, with the rev-parse version, you can look for refs/tags/$1 to get both lightweight and annotated tags, and you can append a ^{tag} to the end to enforce an annotated tag (refs/tags/$1^{tag}).

Also, as mentioned before by @forvaidya, you could simply list the tags and grep for the one you want:

if GIT_DIR=/path/to/repo/.git git show-ref --tags | egrep -q "refs/tags/$1$"
then
    echo "Found tag"
else
    echo "Tag not found"
fi

You can also use git tag --list instead of git show-ref --tags:

if GIT_DIR=/path/to/repo/.git git tag --list | egrep -q "^$1$"
then
    echo "Found tag"
else
    echo "Tag not found"
fi

If you know the tag though, I think it's best just to just look it up via rev-parse. One thing I don't like about the egrep version is that it's possible you could have characters that could get interpreted as regex sequences and either cause a false positive or false negative. The rev-parse version is superior in that sense, and in that it doesn't look at the whole list of tags.

Another option is to use the pattern feature of git show-ref:

if GIT_DIR=/path/to/repo/.git git show-ref --tags "refs/tags/$1" >/dev/null 2>&1
then
    echo "Found tag"
else
    echo "Tag not found"
fi

This avoids the extra egrep invocation and is a bit more direct.

John Szakmeister
  • 44,691
  • 9
  • 89
  • 79
  • Yes, defining the GIT_DIR is a good way! I think they both work. Thanks anyway. – Kim Jul 22 '13 at 17:44
  • 1
    The `rev-parse` option will match a Git "long" tag (generated by `git describe`), but the second option matches only real tags. Thanks! I don't see any advantage in using `git show-ref --tags` over `git tag --list`, though. Does it matter? – big_m Nov 04 '16 at 02:54
  • 1
    @big_m it does not matter. You just need to make sure to match it correctly... so use something like `egrep -q "^$1$"` to match sure you match the entire entry. – John Szakmeister Nov 04 '16 at 07:34
  • @big_m I updated the answer with an extra block showing how you can use the `rev-parse` version to only look for a tag. – John Szakmeister Nov 04 '16 at 07:47
  • Thanks for `^{tag}` suffix tip, @jszakmeister! Could have sworn I tried that, but apparently not (or not correctly anyway). I just tried it again and it works, just as you say. – big_m Nov 04 '16 at 19:32
  • `show-ref` can use a pattern as well, so why not use `git show-ref --tags "$1"` rather than `git show-ref --tags | egrep -q "refs/tags/$1$"` – Oscar Scholten Dec 02 '21 at 12:02
  • @OscarScholten The default behavior is to match starting at the back of the full ref. Let's assume that `$1` was "foo". If you had the tags `foo` and `bar/foo`, `git show-ref --tags "foo"` would show both of them. OTOH, using `git show-ref --tags "refs/tags/$1"` would work, and is a nicer solution. – John Szakmeister Dec 03 '21 at 01:51
20

Here's the rev-parse version developed further:

tag=whatever
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
    echo "found"
else
    echo "not found"
fi

It appears to be robust:

  • Checks only for a tag, not a branch or a commit hash, etc.
  • Weird tag name input doesn't cause weird behavior:
    • Tag names starting with "-" are not mistaken for command line options
    • Tag names containing slashes or dots are not special
    • Tag names containing whitespace are not special
    • Blank tag name isn't special
Lassi
  • 3,522
  • 3
  • 22
  • 34
  • 3
    Unfortunately, if the $tag string ends with "-q" and a string of 7 or more hex digits (such as in the generated "long" tags from `git describe`), this will match on the commit hash, even if there's no such tag. The grep option with `git tag --list` or `git show-ref --tags` is the only option I've found that avoids this. – big_m Nov 04 '16 at 02:52
10

Very Simple Version(use git ls-remote)

TAG_NAME=$1
git ls-remote --exit-code --tags origin $TAG_NAME || echo 'not found'
niceilm
  • 114
  • 1
  • 4
6

The solution I quite like which I think is using a more modern version of git (git version 2.7.4)

#!/usr/bin/env bash
cd /to/repo/base;
tagName="Whatever";

if [[ `git tag -l $tagName` == $tagName ]]; then
    echo "yes";
else
    echo "no";
fi
cad106uk
  • 471
  • 5
  • 5
3

Assuming you're in the project root directory...

# Filename: check-for-tag
# Usage:    check-for-tag <TAG_NAME>
# Example:  check-for-tag ticket-123-fix-this-bug
TAG_NAME=$1
git ls-remote --tags 2>/dev/null | grep $TAG_NAME 1>/dev/null
if [ "$?" == 0 ]; then 
   echo "Git tag $TAG_NAME exists."
else  
   echo "Git tag $TAG_NAME does not exist."
fi
l3x
  • 30,760
  • 1
  • 55
  • 36
1

I use this method to tell if a tag exists for the current revision, to avoid tagging twice.

git_rev_id=$(git -C $REPO_FOLDER rev-parse HEAD)
git_tags=$(git tag)

for git_tag in $git_tags; do
    git_temp_tag=$(git cat-file tag $git_tag | grep $git_rev_id);
    if [ -z "$git_temp_tag" ]
    then
        false; #do nothing
    else
        git_tag_exists=$git_tag
    fi
done

if [ -z "$git_tag_exists" ]
then
  echo "need to make a tag"
else
  echo "Found tag: $git_tag_exits"
fi
doctorhino
  • 11
  • 2