WARNING: if you know nothing about – and/or aren’t interested in – Linux shell, Git, or programming this will probably be a very boring and nonsensical read for you.

I have a script that modifies the shell prompt to display my local Git status if the directory I’m in is a repo. It has a bunch of features – branch name, different colours if the branch is clean vs changed, how many commits I’m ahead/behind, and file stats for new, deleted, modified, renamed, untracked and conflicted. It’s pretty cool.

But it’s also not very efficient. I’m re-calling git status in several places in order to parse its output for the various indicators I want to show, which obviously adds some overhead and makes it take longer. Seeing as this is all re-calculated after every command I run in the shell, being as quick as possible is a must so I’m currently trying to optimise it. I’ve chosen to first only call git status once and just pass it around to the places it needs parsing, and secondly to look at all my regex and see if I can combine/drop/improve any of it.

However, storing the output of git status once to use everywhere is proving more difficult than I first thought. In my code, I’m doing this:

git_ref_name() {
    echo $(git status 2> /dev/null | grep ... # do some regex magic to get branch name
}
...
setup_prompt() {
    ...
    local __git_branch_name=""'$(git_ref_name)'
    ...
    printf "(%s)" "'$__git_branch_name'"
    ...
}

setup_prompt is called every time the shell needs to display the prompt, as in on first load of the terminal and then after every command. This works fine, and always updates the branch name that is shown in my prompt. So, it should work if I do something similar with an actual call to git status, right? Wrong.

git_ref_name() {
    grep -o "... # do some regex magic to get branch name
}
...
setup_prompt() {
    ...
    local __git_status=""'$(git status)'
    local __git_branch_name=""'$(printf "%s" "'$__git_status'" | git_ref_name)'
    ...
    printf "(%s)" "'$__git_branch_name'"
    ...
}

This shows me nothing. Somehow, it’s now returning an empty string instead of a branch name.

I’ve done various experiments over the past day or two, but I can only achieve one of three outcomes:

  • no output
  • some obscure error that’s really hard to track down
  • correct output that doesn’t update when I switch to another branch or change files

From what I’ve tried, it seems that you can only store references to local functions and not actual commands. By reference I mean that rather than storing the output or the command name, it stores a pointer to the command so that it gets called again whenever you re-process the script without closing and re-opening the terminal.

Either that or I’m making some rookie mistake that I understand so poorly none of my Google searching is returning relevant results because I’m not looking for the right thing.

Whatever the case, it’s clear this is going to take a while to figure out…