Customize your ZSH prompt with this one weird trick!
I like having the last commit's sha in the command line. Gives me instant access to the info. I'll walk you through how I made zsh to show it to me.
Glad you clicked through, turns out clickbait headers work! š
I like being pedantic about what my terminal looks like. Iāve recently acquired a new machine to develop on and I recreated my environment from scratch instead of using time machine to migrate my old computer. Main reason being is that I donāt like the cruft that just accumulated over the years however careful I was with what I installed.
Among the first few bits I customised were iTerm2, oh my zsh to be my terminal, and the agnoster theme for it. Yes, I also installed the powerline fonts, though Iām using Fira Code, which has those built in already.
Once iTerm2 is set to solarized light, background is somewhat opaque and blurry, Agnoster set as the theme, and Fira Code is at 16pt size, this is what my terminal looks like:

However what Iām missing is the last commitās short sha after the branch name, so letās do that!
First off, I know itās possible, because Iāve done it in the past. Sadly none of my computers (dating back to 2011) have that setting any more, so need to find how to do it again. From memory I had to mess around with some environment variables in my ~/.zshrc file, but thatās the only thing I have to go on.
Having talked to a bunch of friends on a Discord server, they suggested I check out the agnoster themeās file to see how the prompt is actually being printed. The file itself is ~/.oh-my-zsh/themes/agnoster.zsh-theme, and within there we need to look at the prompt_git function.
At the time of writing thatās a 43 line bash function with a LOT of cryptic things going on. However the very last line is an echo, which means thatās probably the line that I actually see in the terminal. Changing that in place and reloading the terminal confirms that.
The bash command to get the short sha is git rev-parse --short HEAD, which does show up in the actual body of the function but itās sent into oblivion so nothing actually happens.
Then thereās the section about zstyle and vcs_info; two things I had zero idea about.
zstyle
Couldnāt find a lot about it while Googling before I got a better result. My first excursion took me to this StackOverflow article: https://unix.stackexchange.com/questions/214657/what-does-zstyle-do.
In it they talk about how zstyle also handles how whatever vcs_info does ends up looking like.
vcs_info
This is a function that seems to be in zsh internals: https://github.com/zsh-users/zsh/blob/zsh-5.8/Functions/VCS_Info/vcs_info
Thereās a WHOLE lot of documentation around it on the man pages in section 26.5: http://zsh.sourceforge.net/Doc/Release/User-Contributions.html#Version-Control-Information. I donāt precisely remember how I got here, but it involved searching and a lot of quick filter checks to see whether the document has useful info in it or not. Read it, itās useful, however for the tldr crowd:
context
:vcs_info:vcs-string:user-context:repo-root-nameThis controls how specific we get. vcs_info means weāre dealing with that function. vcs-string is the name of the current vcs (version control system) being used. Stuff like git, hg, svn, etc... The documentation has a full list.
user-context is an arbitrary string that gets passed in. Iām not 100% sure what this is or how it works, but we donāt need it.
repo-root-name is the root name of the current repository where we are in.
Putting it all together, a string like this
:vcs_info:git:*:zshmeans that weāre only dealing with git repositories in whatever user context where the repository name is zsh. Itās going to be pretty restrictive.
This one: :vcs_info:* however matches for every folder thatās tracked in any of the 20 or so supported version control systems in all user contexts whatever the repositoriesā names are, ie MATCH ALL THE THINGS!
With that knowledge armed, letās look at what Agnoster does for that and what those lines mean!
What Agnoster does
Hereās this snippet towards the very end of the prompt_git function:
autoload -Uz vcs_info
zstyle ':vcs_info:*' enable git
zstyle ':vcs_info:*' get-revision true
zstyle ':vcs_info:*' check-for-changes true
zstyle ':vcs_info:*' stagedstr 'ā'
zstyle ':vcs_info:*' unstagedstr 'ā'
zstyle ':vcs_info:*' formats ' %u%c'
zstyle ':vcs_info:*' actionformats ' %u%c'
vcs_info
echo -n "${ref/refs\/heads\//$PL_BRANCH_CHAR }${vcs_info_msg_0_%% }${mode}"The autoload line makes sure that the rest of the script calls vcs_info as a function. This is useful in case you have a program called vcs_info and naming conflicts would ensue. Anyways hereās the StackOverflow answer about it with references.
Then the next few lines set context around the function. enable git means itās only going to deal with git repositories (but not hg, svn...).
get-revision true means it will fetch the current revision number (ie last commit sha) and make it available for us.
check-for-changes true means it will pay attention to whether we have uncommitted changes either staged or unstaged.
stagedstr and unstagedstr needs the previous to be true. They control what the string is going to be to signify staged changes and unstaged changes repsectively. They show up as %u and %c.
formats is a string that is used to display THINGS about your current repository. It takes a bunch of placeholders and arbitrary characters. %u%c will display the dot and/or the plus sign if there are staged / unstaged changes present, otherwise it displays nothing.
actionformats is like the previous, but only used when something is currently being done to the repository like a merge conflict, rebase conflict, or interactive rebase.
The next line actually calls the vcs_info function. The autoload line did not call the function, it just made sure that when it is called, the right thing gets called.
And then we echo. Technically the branch name (this thing: ${ref/refs/heads//$PL_BRANCH_CHAR }) is available as %b, but then Agnoster couldnāt put the branch icon before the branch name.
This also means that the branch icon and branch name come first, then whatever the vcs_info wants to output, then ${mode} (signifies git action like rebase >R>, bisect <B>, or merge >M<).
Override
By far the easiest solution would be to edit the theme file, but I donāt like to do that for many reasons. One of them is I keep zsh updated regularly and any update would wipe my changes, therefore anything I do would need to happen inside my own .zshrc file.
I copied the vcs_info formats line into there to see what happened, reloaded the terminal, and lo and behold, my changes show up!
# this is in .zshrc somewhere
zstyle ':vcs_info:git:*' formats ' herp'
%i gets us the revision, but itās the long revision.

From there itās a question of truncating that and getting the first 8 characters. Googling further, I found that %8.8i will achieve what I want, but I have no idea why. I think it has to do with printf formatting, but couldnāt actually find anything concrete there. Something about width / decimals maybe.
One more niggly bit was that I donāt need to see my user@computername bit, because itās not relevant to me, and it takes up space.
Putting the following line into .zshrc as well, but strictly after the source line makes the user@computer (context) disappear.
# in .zshrc file
source $ZSH/oh-my-zsh.sh # this is already in there, find it
prompt_context(){}And with these two changes, my terminal looks the way I want it to look like!

Conclusion
Thereās not much. Read documentation. Experiment! The terminal is your oyster! Let me know if you found it useful!
Photo by Victor Garcia on Unsplash