iamnewton
iamnewton

Reputation: 339

Setting color on git status in bash prompt

I'm trying to set my bash prompt to with the following script. Everything works, but the part where I print out the branch name in a Git repository and the status of the branch in color. The colors are somewhat arbitrary, but needless to say it would be red if any files are uncommitted, or yellow if files are unstaged, and green for anything else. It's printing out the part i want in white thought. When I run the part of the script where, towards the end, I define $branchStyle individually, it works, but its not within here. What am I doing wrong?

prompt_git() {
  local s=""
  local branchName=""

  # check if the current directory is in a git repository
  if [ $(git rev-parse --is-inside-work-tree &>/dev/null; printf "%s" $?) == 0 ]; then

      # check if the current directory is in .git before running git checks
      if [ "$(git rev-parse --is-inside-git-dir 2> /dev/null)" == "false" ]; then

          # ensure index is up to date
          git update-index --really-refresh  -q &>/dev/null

          # check for uncommitted changes in the index
          if ! $(git diff --quiet --ignore-submodules --cached); then
              s="$s+";
          fi

          # check for unstaged changes
          if ! $(git diff-files --quiet --ignore-submodules --); then
              s="$s!";
          fi

          # check for untracked files
          if [ -n "$(git ls-files --others --exclude-standard)" ]; then
              s="$s?";
          fi

          # check for stashed files
          if $(git rev-parse --verify refs/stash &>/dev/null); then
              s="$s$";
          fi

      fi

      # get the short symbolic ref
      # if HEAD isn't a symbolic ref, get the short SHA
      # otherwise, just give up
      branchName="$(git symbolic-ref --quiet --short HEAD 2> /dev/null || \
                  git rev-parse --short HEAD 2> /dev/null || \
                  printf "(unknown)")"

      [ -n "$s" ] && s=" [$s]"

      printf "%s" "$1$branchName$s"
  else
      return
  fi
}

set_prompts() {

    local bold=$(tput bold)
    local reset=$(tput sgr0)
    local base05=$(tput setaf 188) # light grey
    local base08=$(tput setaf 210) # red
    local base0A=$(tput setaf 221) # yellow
    local base0B=$(tput setaf 114) # green

    if git rev-parse --git-dir >/dev/null 2>&1; then
        # check for uncommitted changes in the index
        if ! git diff-index --quiet --cached HEAD --ignore-submodules -- >&2; then
            branchStyle=$base08
        # check for unstaged changes
        elif ! git diff-files --quiet --ignore-submodules -- >&2; then
            branchStyle=$base0A
        else
            branchStyle=$base0B
        fi
    fi

    PS1+="\$(prompt_git \"$bold$base05 on $branchStyle\")" # git repository details

    export PS1
}

set_prompts
unset set_prompts

Upvotes: 3

Views: 2180

Answers (2)

that other guy
that other guy

Reputation: 123690

A good approach to any problem is to reduce it to the minimal piece of code required to reproduce the issue you are seeing. This makes it easier to debug, easier for others to read and solve, and makes for a more general question that other users can benefit from.

For example, if you look into your problem, you'll see that:

  • It's not related to the prompt_git function: the same thing happens when replacing it with a simple echo.
  • It's not related to colors: the same thing happens if you use a plain string.
  • It's not related to unstaged or uncommited files: the same thing happens if you skip those checks.
  • It's not related to git at all: the same thing happens if you just compare directories.

Now we can write a question with a small code sample that shows exactly what's wrong:

I expected the following piece of code to show "in /tmp" when I cd /tmp, but instead the prompt remains blank. Why doesn't it update?

set_prompts() { 
  message=""
  if [[ $PWD/ == /tmp/* ]]
  then
    message="in /tmp"
  fi
  PS1="\$(echo $message) \$"
}
set_prompts
unset set_prompts

This question is significantly easier to read and answer: set_prompts runs once and calculates a value for $message, and then it's unset and never executed again. This is why the message never changes.

To make it work, make sure to re-run it before every prompt to regenerate it. This can be done with PROMPT_COMMAND:

set_prompts() { 
  PS1='\u@\h:\w '                 # <-- Reset PS1 before each run
  message=""
  if [[ $PWD/ == /tmp/* ]]
  then
    message="in /tmp"
  fi
  PS1="\$(echo $message) \$"
}    
PROMPT_COMMAND='set_prompts'      # <-- Run function before every prompt

This works as expected, and can be easily adapted to your actual, long piece of code.

Upvotes: 3

Brad
Brad

Reputation: 3540

Not all terminals seem to support the 3 digit numbers, but some are restricted to the portable 8 colors declared in man terminfo:

Color       #define       Value       RGB
black     COLOR_BLACK       0     0, 0, 0
red       COLOR_RED         1     max,0,0
green     COLOR_GREEN       2     0,max,0
yellow    COLOR_YELLOW      3     max,max,0
blue      COLOR_BLUE        4     0,0,max
magenta   COLOR_MAGENTA     5     max,0,max
cyan      COLOR_CYAN        6     0,max,max
white     COLOR_WHITE       7     max,max,max

So, I think your problem is the use of the higher color numbers. You can output a color table for your terminal like this:

( x=`tput op` y=`printf %$((${COLUMNS}-6))s`;for i in {0..256};do o=00$i;echo -e ${o:${#o}-3:3} `tput setaf $i;tput setab $i`${y// /=}$x;done; )

from http://www.commandlinefu.com/commands/view/6533/print-all-256-colors-for-testing-term-or-for-a-quick-reference

Upvotes: 1

Related Questions