Home > Enterprise >  Programmatically identify branch vs lightweight tag vs annotated tag
Programmatically identify branch vs lightweight tag vs annotated tag

Time:06-14

I'm writing a Bash script to output info about references in multiple Git repositories. So far it looks like this -

# paths that have Git repos
paths[0]="./playground"
paths[1]="./sandbox"

# reference objects common to all repos
references[0]="main"
references[1]="v1"
references[2]="v2"
references[3]="v3"

echo "Object type of reference in each repository -"
for reference in "${references[@]}"
do
    echo "~~~ $reference ~~~"
    for path in "${paths[@]}"
    do
        OBJECT_TYPE=$(git -C "$path" cat-file -t "$reference")
        if [ "$OBJECT_TYPE" = "tag" ]; then
            prefix="annotated"
        #else
            #TODO: implement logic for lightweight tags, branches
        fi
        echo "$prefix $OBJECT_TYPE $path"
    done
    echo
done

The annotated tag part is working correctly. However, the $OBJECT_TYPE for branches and lightweight tags are both "commit", which is as expected, but not helpful for identifying which references are branches and which are lightweight tags. How can they be distinguished?

CodePudding user response:

If you only deal with the usual short names for tags and branches (e.g: master and v1, as opposed to refs/heads/master or refs/tags/v1), you can check whether the existing ref if refs/heads/<name> or refs/tags/<name> :

#!/bin/bash

is_branch () {
   local path=$1
   local name=$2

   # use git rev-parse to check if 'refs/heads/$name' exists
   #  * '-q' silences the errors
   #  * if it finds the reference, 'rev-parse' always prints the resulting sha on stdout
   #    (no option to silence that), hence the '> /dev/null'
   git -C "$path" rev-parse --verify -q refs/heads/"$name" > /dev/null
   return $?
}

is_tag () {
   local path=$1
   local name=$2

   git -C "$path" rev-parse --verify -q refs/tags/"$name" > /dev/null
   return $?
}

# or the shorter versions :
is_branch () {
    git -C "$1" rev-parse --verify -q refs/heads/"$2" > /dev/null
}
is_tag () {
    git -C "$1" rev-parse --verify -q refs/tags/"$2" > /dev/null
}


if is_branch "$path" "$reference"; then
  echo "'$reference' is a branch in '$path'"
fi
if is_tag "$path" "$reference"; then
  echo "'$reference' is a tag in '$path'"
fi

CodePudding user response:

git for-each-ref refs/heads --points-at "${reference}" --format="%(refname)" | grep refs/heads/${reference}
git for-each-ref refs/tags --points-at "${reference}" --format="%(refname)" | grep refs/tags/${reference}

The command lists all tags and branches that point at the reference commit. Without --format="%(refname)", the output is like

96e195a92048272cdabd2992a21593bb3774e508 commit refs/heads/master
96e195a92048272cdabd2992a21593bb3774e508 commit refs/tags/nice

With it, it's like

refs/heads/master
refs/tags/nice

Test if the grep result is equal to the reference.

branch=$(git for-each-ref refs/heads --points-at "${reference}" --format="%(refname)" | grep refs/heads/${reference})
tag=$(git for-each-ref refs/tags--points-at "${reference}" --format="%(refname)" | grep refs/tags/${reference})
if [[ "${branch}" = refs/heads/${reference} ]];then
    prefix="branch"
elif [[ "${tag}" = refs/tags/${reference} ]];then
    prefix="lightweight"
else
    prefix="unknown"
fi

Multiple references may point at the same commit, so here grep is used to filter the output of git for-each-ref.

The if-else cannot work well if the repository happens to have both refs/heads/foo and refs/tags/foo. But this naming should always be avoided.

  • Related