nodakai
nodakai

Reputation: 8001

Git: want to know whether a given string refers to a commit-ish or a (possibly remote) branch

Background

I'm debugging a script which wraps several Git operations, the most significant of which is running git checkout <specifier>

As you know git checkout accepts either a commit-ish or a branch name:

The script was written based on the "flexibility" (though I now have to say it was an awful idea but anyways...). Due to the "flexibility", the upstream stage sends <specifier> without any metadata on what it means; it can either be a SHA-1 shorthand like 8e13bb8 or a (remote) branch name such as feature/optimize-foo. In the latter case, it might not exist as a local branch name at the point of receiving it; git checkout is so flexible that it can create a local tracking branch automatically (see the command reference above, a paragraph which begins with "If <branch> is not found but there does exist a tracking branch in exactly one remote")

Problem

Now I need to insert some extra operations only when <specifier> was a (remote) branch name but I'm struggling to find a way to do so. Something along the lines of

if git this-is-branch-name $specifier; then
    git checkout $specifier
    git pull
else
    git checkout --detach $specifier
fi

(The above example does not reflect the complexity of the actual script I'm dealing with, so please don't "recommend" me nicer ways to write it)

Note

Upvotes: 5

Views: 749

Answers (3)

jthill
jthill

Reputation: 60255

Now I need to insert some extra operations only when <specifier> was a (remote) branch name

The usual full spelling of remote branch names beginsrefs/remotes/ (nobody sane ever changes that, I'm just pointing out that it can be done), and the full spelling of all local branch names begins refs/heads.

So

fullspell=`git rev-parse --symbolic-full-name $specifier --`

will set fullspell to the full symbolic name of $specifier if it's purely a symbolic ref, or empty if it's a valid ref but not just a name. case has some useful syntax if you want the same code for multiple cases,

if fullspell=`git rev-parse --symbolic-full-name $specifier --`; then
case $fullspell in
refs/heads/* | refs/remotes/*)
        # it's a remote-tracking branch or a local branch
        ;;
?*)
        # it's something else, but it's a legit refname
        ;;
*)
        # there's no full spelling but the command succeeded. It's not a bare refname,
        # but it does resolve to something (so e.g. master~3 would get here)
        ;;
esac
fi

Upvotes: 3

nodakai
nodakai

Reputation: 8001

Based on @VonC's answer https://stackoverflow.com/a/62338364

I'll probably go with something like

rp=$(git rev-parse --verify -q "$specifier" || true)
if [[ -n "$rp" ]]; then  # if rp is non-empty
   if [[ "$rp" = "specifier"* ]]; then  # if specifier is a prefix of rp
       echo "'${specifier}' is a commit"
   else
       echo "'${specifier}' is a branch"
   fi
else
   echo "'${specifier}' is NEITHER a branch or a commit"
fi

Upvotes: 1

VonC
VonC

Reputation: 1323313

There are way to check if a local or remote branch exists

git rev-parse -q --verify <abranch>

The --verify option, combined with -q, will exit with non-zero status silently if the branch does not exist.

So instead of:

if git this-is-branch-name $specifier; then

use:

if git rev-parse --verify -q $specifier >/dev/null; then

I want to take different actions depending on whether is 1) a commit-ish such as 8e13bb8 or 2) a (remote) branch name such as feature/optimize-foo

That would be:

 if git cat-file -e "${specifier}"; then
   echo "'${specifier}' is either a branch or a commit"
   rp=$(git rev-parse ${specifier})
   if [[ "${rp#${specifier}}" != "${rp}" ]]; then
     echo "'${specifier}' is a commit"
   else
     echo "'${specifier}' is a branch"
   fi
 else
   echo "'${specifier}' is NEITHER a branch or a commit"
 fi

Upvotes: 2

Related Questions