I was watching Lorna Mitchell‘s Open Source Hour twitch stream this morning, and noticed that she had a line in her prompt showing what her git status was.
Git, for those of you who aren’t aware, is the version control software which has dominated software development and documentation for over 10 years now. It’s used for almost everything now, supplanting it’s competitors like Subversion, Visual Source Safe, Mercurial and Bazaar. While many people are only aware of Git using GitHub, before there was GitHub, there was the Git command line. I’m using the git
command in a Bash shell all the time because I find it easier to use that for the sorts of things I do, than it is to use the GUI tools.
However, the thing that often stumbles me is what state I’m in with the project, and this line showed me just how potentially powerful this command can be.
During the video, I started researching how I could get this prompt set up on my machine, and finally realised that actually, git prompt was installed as part of the git
package on my Ubuntu 20.04 install. To use it, I just had to add this string $(__git_ps1)
into my prompt. This showed me which branch I was on, but I wanted more detail than that!
So, then I started looking into how to configure this prompt. I found this article from 2014, called “Git Prompt Variables” which showed me how to configure which features I wanted to enable:
GIT_PS1_DESCRIBE_STYLE='contains'
GIT_PS1_SHOWCOLORHINTS='y'
GIT_PS1_SHOWDIRTYSTATE='y'
GIT_PS1_SHOWSTASHSTATE='y'
GIT_PS1_SHOWUNTRACKEDFILES='y'
GIT_PS1_SHOWUPSTREAM='auto'
To turn this on, I edited ~/.bashrc
(again, this is Ubuntu 20.04, I’ve not tested this on CentOS, Fedora, Slackware or any other distro). Here’s the lines I’m looking for:
I edited each line starting PS1=
, to add this: $(__git_ps1)
, so this now looks like this:
I’m aware that line is pretty hard to read in many cases, so here’s just the text for each PS1 line:
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w$(__git_ps1)\[\033[00m\]\$ '
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w$(__git_ps1)\$ '
The first of those is the version that is triggered when if [ "$color_prompt" = yes ]
is true, the second is when it isn’t.
What does this look like?
Let’s run through a “standard” work-flow of “conditions”. Yes, this is a really trivial example, and quite (I would imagine) different from how most people approach things like this… but it gives you a series of conditions so you can see what it looks like.
Note, as I’m still running a slightly older version of git, and I’ve not adjusted my defaults, the “initial” branch created is still called “master”, not “main”. For the purposes of this demonstration, it’s fine, but I should really have fixed this from the outset. My apologies.
First, we create and git init
a directory, called git_test
in /tmp
.
Next, we create a file in here. It’s unstaged.
And then we add that to the staging area.
Next we commit the file to the repository.
We add some content to the README file.
We realise that we can’t use this change right now, let’s stash it for later.
We check out a new branch, so we can use that stash in there.
And then pop the stashed file out.
We then add the file and commit it.
How about working with remote sources? Let’s change to back to the /tmp
directory and fork git_test
to git_local_fork
.
We’ve checked it out at the feature branch instead of master, let’s check out master instead.
In the meantime, upstream, someone merged “My-New-Feature” into “master” on our original git_test
repo.
On our local branch again, let’s fetch the state of our “upstream” into git_local_fork
.
And then pull, to make sure we’re in-line with upstream’s master
branch.
We should probably make some local changes to this repository.
Meanwhile, someone made some changes to the upstream repository.
So, before we try and push, let’s quickly fetch their tree to see what’s going on.
Oh no, we’ve got a divergence. We need to fix this! Let’s pull the upstream master
branch.
Let’s fix the failed merge.
I think we’re ready to go with the merge.
If the remote were a system like github, at this point we’d just do a git push
. But… it’s not, so we’d need to do a git pull /tmp/git_local_fork
in /tmp/git_test
and then a git fetch
in /tmp/git_local_fork
… but that’s an implementation detail 😉
Featured image is “Status” by “Doug Letterman” on Flickr and is released under a CC-BY license.