zsh prompt: check whether inside git repository and not being ignored by git

git check-ignore . will fail with exit code 128 if . isn’t in a git repository (or any other error occurs), and with exit code 1 only if the path isn’t ignored. So you can check for the latter only:

git check-ignore -q . 2>/dev/null; if [ "$?" -ne "1" ]; then ... 

Inside the then, you’re handling the case where . is ignored or not in a git repository.

To make this work across file system boundaries, set GIT_DISCOVERY_ACROSS_FILESYSTEM to true:

GIT_DISCOVERY_ACROSS_FILESYSTEM=true git check-ignore -q . 2>/dev/null; if [ "$?" -ne "1" ]; then ...

First it should be:

if
  git rev-parse --is-inside-work-tree >/dev/null 2>&1 &&
    ! git check-ignore . >/dev/null 2>&1
then...

Command substitution ($(...)) is to retrieve the output of a command. Here, there's no output as you're redirecting it to /dev/null and if there were, it would be taken as a command to execute (and check its exit status).

Note that the second command is only run if the first one succeeded.

@StephenKitt has shown how to factorize those two commands into one git command, but if that still doesn't help wrt performance, you could always cache the information in an associative array.

typeset -A is_git # declared globally in your ~/.zshrc
if (( ! $+is_git[$PWD] )); then
  git check-ignore -q . 2> /dev/null
  is_git[$PWD]=$(( $? == 1 ))
fi

if (( $is_git[$PWD] )); then...

That cache could become stale if you rename directories, delete .git directories, or change your list of ignored files. You can invalidate it with is_git=() or by restarting zsh with exec zsh.

Another option would be to make that check only when you enter a directory using the chpwd hook:

check_git() {
  git check-ignore -q . 2> /dev/null
  (( is_git = $? == 1 ))
}
chpwd_functions+=(check_git)

And then, in your prompt routine, it's just a matter of:

if (( is_git )); then...

Tags:

Git

Prompt

Zsh