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.