Revisiting bashrc

I decided to try to have ChatGPT help me optimize my .bashrc file. The results were very mixed.

terminal window showing a colorful prompt reflecting the status of a git repository.

I wrote in the past about Showing Status in the Git Bash Prompt. It's one of my more popular posts. One thing I have noticed is that the solution I proposed there was always a little slow. It was manageable, but recently I was working on a particular machine, and it was ungodly slow, and I was like "Alright, time to fix it."

Now, I originally found that solution through some Googling/Stack Overflow, and sure, I modified it for my own needs, but I never had a deep understanding of how it worked, and I'm not entirely sure it's necessary to have a deep understanding of it. I really just need it to work, and in this particular case, work a little faster, so it seemed like a great case for AI.

I know enough bash to be able to check the output. Verifying the output would be pretty easy, and the consequences of AI getting it wrong were pretty minimal - I could always revert back to what I had. I also had an idea of what was wrong with the script - namely that it ran git status on directories that didn't contain git repositories and that it ran the status more than once (which in the end turned out not to be as big a performance hit as I thought). So I thought, why not?

AI -meh... kinda useful

So I uploaded my .bashrc file to ChatGPT, and I was unimpressed (at least at first).

Apparently, ChatGPT didn't even look in the file because the line it flagged doesn't exist in my .bashrc file. No, I'm not exaggerating. It literally does not exist. Apparently, writing this type of function is a common way to display git status on the command prompt, but it's not the way I was doing it.

So I corrected ChatGPT.

And it proceeded to point out what I already know. Although there was one thing it pointed out that I hadn't realized.

That one change did make a difference, and I wouldn't have picked up on that on my own. So, ok, ChatGPT gets a little credit for that, although the function call was formatted wrong.

How do you define a function in bash?

All of ChatGPT's suggestions involved writing some bash functions. I did end up using something similar to this screenshot- you can see my completed .bashrc file below. However, it did require some modifications, since ChatGPT doesn't know the syntax for declaring functions in bash. Yeah, you read that right.

Initially, this seemed really odd to me, since ChatGPT should have lots of bash function definitions in its training data. Then I remembered that it's just a synthetic text extrusion machine with no thought process, and I guess it makes sense that it would use a C-style function definition since that is statistically more likely to occur in its training data. Just a reminder that it doesn't really have any context or understanding.

I did learn about the PROMPT COMMAND variable and PS1 - although not from GPT. I just googled it. But, ChatGPT did point me in the right direction, so maybe a 1/2 kudo there.

Didn't Quite Work

The solution suggested calling `git status` only once. What it missed from my original file, and at first glance, I missed as well, was that the two git status calls had different parameters. One uses a -s. So following that advice broke things. So minus one for ChatGPT there - back to zero. I set it back to 2 separate calls to git status, and now it all works. And it is snappier than what it was, so overall a win.

New File

Here is the result. I just pasted the whole .bashrc file here. There are some other sections besides the prompt section. I thought you all might find them useful. The first section sets up SSH caching (useful if you have a password on your ssh file - you only have to enter it once when you launch the shell)- I believe I got that off Stack Overflow, but unfortunately, I don't have the link. The #PS1 section is the section you want for the prompt. The last section sets up some useful aliases.

#set -euo pipefail


#SSH Setup Section
env=~/.ssh/agent.env

agent_load_env () { test -f "$env" && . "$env" >| /dev/null ; }

agent_start () {
    (umask 077; ssh-agent >| "$env")
    . "$env" >| /dev/null ; }

agent_load_env

# agent_run_state: 0=agent running w/ key; 1=agent w/o key; 2= agent not running
agent_run_state=$(ssh-add -l >| /dev/null 2>&1; echo $?)

if [ ! "$SSH_AUTH_SOCK" ] || [ $agent_run_state = 2 ]; then
    agent_start
    ssh-add
elif [ "$SSH_AUTH_SOCK" ] && [ $agent_run_state = 1 ]; then
    ssh-add
fi

unset env
#END SSH Setup Section

#PS1 - Command Prompt Display Setup
# from https://medium.com/@damianczapiewski/how-to-pimp-up-the-git-bash-prompt-on-windows-without-any-external-stuff-c69eb9ef0125

git_stats() {

  local STATUS=$(git status -s 2>/dev/null)
  local UNTRACKED=$(echo "$STATUS" | grep '^??' | wc -l)
  local STAGED=$(($(echo "$STATUS" | grep '^M ' | wc -l) + $(echo "$STATUS" | grep '^D ' | wc -l) + $(echo "$STATUS" | grep '^R ' | wc -l) + $(echo "$STATUS" | grep '^C ' | wc -l)+$(echo "$STATUS" | grep '^A ' | wc -l)))
  local DRC=$(($(echo "$STATUS" | grep '^ D' | wc -l) + $(echo "$STATUS" | grep '^ R' | wc -l) + $(echo "$STATUS" | grep '^ C' | wc -l)))
  local MODIFIED=$(echo "$STATUS" | grep '^ M' | wc -l)
  local STATS=''
  if [ $UNTRACKED != 0 ]; then
    STATS="\e[43m untr: $UNTRACKED "
  fi
  if [ $MODIFIED != 0 ]; then
    STATS="$STATS\e[43m mod: $MODIFIED "
  fi
  if [ $DRC != 0 ]; then
    STATS="$STATS\e[43m drc: $DRC "
  fi
  if [ $STAGED != 0 ]; then
    STATS="$STATS \e[42m staged: $STAGED "
  fi
  if [ ! -z "$STATS" ]; then
  echo -e "\e[30m $STATS\e[0m"
  fi
}

function origin_dist {

 local STATUS=$(git status 2>/dev/null)
 local DIST_STRING=""
 local IS_AHEAD=$(echo -n "$STATUS" | grep "ahead")
 local IS_BEHIND=$(echo -n "$STATUS" | grep "behind")
 local DIVERGED=$(echo -n "$STATUS" | grep "diverged")
 if [ ! -z "$IS_AHEAD" ]; then
  local DIST_VAL=$(echo "$IS_AHEAD" | sed 's/[^0-9]*//g')
  DIST_STRING="$DIST_VAL AHEAD"
 elif [ ! -z "$IS_BEHIND" ]; then
  local DIST_VAL=$(echo "$IS_BEHIND" | sed 's/[^0-9]*//g')
  DIST_STRING="BEHIND $DIST_VAL"
 elif [ ! -z "$DIVERGED" ]; then
    DIST_STRING="DIVERGED"
 fi
 if [ ! -z "$DIST_STRING" ]; then
  echo -en "\e[97;45m $DIST_STRING"
 fi
}

build_prompt() {
  local BASE='\n\n\[\e[97;104m\] \u \[\e[30;43m\] \w \[\e[97;45m\]'
  local AFTER='\[\e[0m\]\n\n>> '
  if git rev-parse --is-inside-work-tree &>/dev/null; then
    local BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
    local DIST=$(origin_dist)
    local STATS=$(git_stats)
    PS1="${BASE} ${BRANCH} ${DIST} ${STATS} ${AFTER}"
  else
    PS1="${BASE}${AFTER}"
  fi
}

PROMPT_COMMAND=build_prompt

# END PS1

export PATH=$PATH:"/c/Program Files/G-CLI"

function git_changed() {(git diff --name-only --diff-filter=MA $(git merge-base --fork-point $1); git ls-files --others --exclude-standard)}

function blue_changed() {(git_changed $1 | xargs -rd '\n' g-cli blue -- -d) }

alias lg=lazygit
alias blue='g-cli blue -- -d'
source ~/.glab_completion.sh
source ~/.tldr_completion.sh

Is ChatGPT that useful?

The short answer is: Maybe? I did end up with a more efficient, better-performing Bash prompt. However, I'm pretty sure I could have figured that out on my own in a similar time frame using Google and Stack Overflow. Any speed made up by how quickly ChatGPT was able to produce an answer was killed by the debugging. If I didn't have any existing Bash knowledge, I don't think I would have gotten there with ChatGPT alone. An occasionally helpful, bumbling assistant for an expert? Maybe. Helpful for complete beginners? Doubtful.