The importance of a lean PROMPT
I recently updated my dotfiles with a fundamental change in how I display git-related information in my shell’s prompt.
In particular, my PROMPT environment variable contained a call to:
function parse_git_branch() {
git branch 2> /dev/null | sed -n -e 's/^\* \(.*\)/[\1]/p'
}
and now, for git repositories, it does:
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
local ref
ref=$(git branch --show-current 2>/dev/null)
if [[ -n "$ref" ]]; then
echo "git [$ref]"
else
echo "git [$(git rev-parse --short HEAD 2>/dev/null)]"
fi
return
fi
echo "[no_vcs]"
Commonalities
What both commands have in common is error redirect, or:
2>file
where file in this case is /dev/null, which simply discards the data it receives.
Previous prompt
My previous prompt was naively doing:
- list all git branches (
git branch, equivalent togit branch --list) - never print errors
- pipe git branches into
sed(s/pattern/replacement/flags)-n: do [n]ot print input-e: use a sed [e]xpression- match regex pattern
/^\* \(.*\)/(asterisk followed by a space and something else, like* main, and capture group after space and asterisk) - replace match with capture group 1 in square brackets (
[\1]) /pflag means “print if successful”
So, it would list all branches, and then find the current one (if any, marked with an asterisk by git), and wrap it in square brackets.
Current prompt
The current prompt makes use of rev-parse and branch --show-current instead of branch (--list).
Despite its cryptic man page, git’s rev-parse is a simple query command to get information about the repository (branches, hashes, root, etc.). From it, we leverage:
--is-inside-work-tree: returnstrueif CWD is inside the git repository that git attempts to find when launching the CLI (typically, a.git/dir in CWD or parent directories)
and from git branch:
git branch --show-current: returns the current branch, or empty string if none. Should takeO(1)instead ofO(#branches).
Speed improvement
The performance in my prompt was of 5x, measured with GNU/BSD time (/usr/bin/time) and iterating 100 times as a rough estimate:
$ /usr/bin/time -p zsh -c '
for i in {1..100}; do
<command>
done
'
making it from total time elapsed of 4.38s to 0.87s, in a repo with a badly managed history:
- 1.4GB of
.git/directory, and - 254 tracked branches
effectively, managing the repo usable in my current shell (zsh).
JJ Prompt
JJ (jujutsu), a version control system I found out thanks to the great Steve Klabnik (steveklabnik.com), is my go-to CLI to manage history of software projects. It has git compatibility, so I can use it with current projects hosted in git-compatible servers (Github, Gitlab, Bitbucket, etc.).
I have started adding some jj information to my prompt, with:
if [[ -d .jj ]]; then
local jj_id
jj_id=$(jj log -r @ --no-graph --ignore-working-copy --template 'change_id.short()' 2>/dev/null) || jj_id="err"
echo "jj [$jj_id]"
return
fi
Which looks for a .jj/ directory and, if present, gets the current short id of the current (@) commit.
With time, I might add some more information. However, I will only if I see that the commands needed are lightweight, as this experiment has proven to me that having a lean PROMPT is essential to have a usable shell.
~mpsanchis