Skip to content

Instantly share code, notes, and snippets.

@basus
Created January 6, 2012 03:10
Show Gist options
  • Save basus/1568754 to your computer and use it in GitHub Desktop.
Save basus/1568754 to your computer and use it in GitHub Desktop.
Org2blog broken shortcode export
I've been a sworn fan of version cont
[sourcecode language="text" light="true"]
function gitify {
status=$(git status 2>/dev/null | tail -n 1)
if [[ $status == "" ]]
then
echo ""
else
echo $(git-
[sourcecode language="text" light="true"]
function make-prompt
{
local RED="\[\033[0;31m\]"
local GREEN="\[\033[0;32m\]"
local LIGHT_GRAY="\[\033[0;37m\]"
local CYAN="\[\033[36m\]"
PS1="${CYAN}\h\
${GREEN} \w\
${RED} \$(gitify)\
${GREEN} >\
${LIGHT_GRAY} "
}[/sourcecode]
ranch-name)$(git-dirty)$(git-unpushed)
fi
}[/sourcecode]
sourcecode language="text" light="true"]
function git-dirty {
st=$(git status 2>/dev/null | tail -n 1)
if [[ $st != "nothing to commit (working directory clean)" ]]
then
echo "*"
fi
}[/sourcecode]
ol for a good few
[sourcecode language="text" light="true"]
function git-branch-name
{
echo $(git symbolic-ref HEAD 2>/dev/null | awk -F/ {'print $NF'})
}[/sourcecode]
ears now. After a brief flirtation with Subversion I am currently in a long term and very committed relationship with the Git version control system. I use Git to store all my code and writing and to keep everything in sync between my machines. Almost everything I do goes into a repository.
When I'm working I spend most of my time in three applications: a text editor (generally Emacs), a terminal (either iTerm2 or Gnome Terminal) and a browser (Firefox or Safari). When in Emacs I use the excellent Magit mode to keep track of the status of my current project repository. However my interaction with git is generally split between Emacs and the terminal. There's no real pattern, just what's easiest and open at the moment. Unfortunately when I'm in the terminal there's no visible cue as to what the status of the repo is. I have to be careful to run <code>git status</code> regularly to see what's going. I need to manually make sure that I've committed everything and pushed to the remote server. Though this isn't usually a problem, every now and then I'll forget to commit and push something on one of my machines, go to another and then realized I've left behind all my work. It's annoying and kills productivity.
Over the last few days I decided to sit down and give my terminal a regular indicator of the state of the current repository. So without further ado, here's how I altered my Bash prompt to show relevant Git information.
<div id="outline-container-1" class="outline-3">
<h3 id="sec-1">Extracting Git information</h3>
<div id="text-1" class="outline-text-3">
There are generally three things I'm concerned about when it comes the Git repo I'm currently working on:
<ol>
<li>What is the current branch I'm on?</li>
<li>Are there any changes that haven't been committed?</li>
<li>Are there local commits that haven't been pushed upstream?</li>
</ol>
Git provides a number of tools that gives you a lot of very detailed information about the state of the repo. Those tools are just a few commands away and I don't want to be seeing everything there is to be seen at every step. I just want the minimum information to answer the above question.
Since the bash prompt is always visible (and updated after each command) I can put a small amount of text in the prompt to give me the information I want. In particular my prompt should show:
<ol>
<li>The name of the current branch</li>
<li>A "dirty" indicator if there are files that have been changed but not committed</li>
<li>The number of local commits that haven't been pushed</li>
</ol>
</div>
</div>
<div id="outline-container-2" class="outline-3">
<h3 id="sec-2">What is the current branch?</h3>
<div id="text-2" class="outline-text-3">
The <code>symbolic-ref</code> command shows the branch that the given reference points to. Since HEAD is the symbolic reference for the current state of the working tree, we can use <code>git symbolic-ref HEAD</code> to get the full branch. If we were on the <code>master</code> branch we would get back something like <code>refs/heads/master</code>. We use a little Awk magic to get rid of everything but the part after the last /. Wrapping this into a litte function we get:
<pre class="example">function git-branch-name
{
echo $(git symbolic-ref HEAD 2&gt;/dev/null | awk -F/ {'print $NF'})
}</pre>
</div>
</div>
<div id="outline-container-3" class="outline-3">
<h3 id="sec-3">Has everything been committed?</h3>
<div id="text-3" class="outline-text-3">
Next we want to know if the branch is dirty, i.e. if there are uncommitted changes. The <code>git status</code> command gives us a detailed listing of the state of the repo. For our purposes is the very last line of the output. If there are no outstanding changes it says "nothing to commit (working directory clean)". We can isolate the last line using the Unix <code>tail</code> utility and if it doesn't match the above message we print a small asterisk (*). This is just enough to tell us that there is something we need to know about the repo and should run the full <code>git status</code> command.
Again, wrapping this all up into a little function we have:
<pre class="example">function git-dirty {
st=$(git status 2&gt;/dev/null | tail -n 1)
if [[ $st != "nothing to commit (working directory clean)" ]]
then
echo "*"
fi
}</pre>
</div>
</div>
<div id="outline-container-4" class="outline-3">
<h3 id="sec-4">Have all commits been pushed?</h3>
&lt;div class="outl
[sourcecode language="text" light="true"]
function git-unpushed {
brinfo=$(git branch -v | grep git-branch-name)
if [[ $brinfo =~ ("[ahead "([[:digit:]]*)]) ]]
then
echo "(${BASH_REMATCH[2]})"
fi
}[/sourcecode]
Since we already know the name of the branch we're on, we use <code>grep</code>to isolate the line that tells us about our branch of interest. If we have local commits that haven't been pushed the status line will say something like ="[ahead X]"=, where X is the number of commits not pushed. We want to get that number.
Since what we're looking for is a very well-defined pattern I decided to use BASH's built-in regular expressions. I provide a pattern that matches ="[ahead X]" where X is a number. The matching number is stored in the BASH_REMATCH array. I can then print the number or nothing if no such match is present in the status line. The function we get is this:
<pre class="example">function git-unpushed {
brinfo=$(git branch -v | grep git-branch-name)
if [[ $brinfo =~ ("[ahead "([[:digit:]]*)]) ]]
then
echo "(${BASH_REMATCH[2]})"
fi
}</pre>
The =~ is the BASH regex match operator and the pattern used follows it.
</div>
<div id="outline-container-5" class="outline-3">
<h3 id="sec-5">Assembling the prompt</h3>
<div id="text-5" class="outline-text-3">
All that's left is to tie together the functions and have them show up in the BASH prompt. I used a little function to check if the current directory is actually part of a repo. If the <code>git status</code> command only returns an error and nothing else then I'm not in a git repo and the functions I made would only give nonsense results. This functions checks the <code>git status</code> and then calls the other functions or does nothing.
<pre class="example">function gitify {
status=$(git status 2&gt;/dev/null | tail -n 1)
if [[ $status == "" ]]
then
echo ""
else
echo $(git-branch-name)$(git-dirty)$(git-unpushed)
fi
}</pre>
Finally we could put together prompt. BASH allows for some common system information to be displayed in the prompt. I like to see the current hostname (to know which machine I'm on if I'm working over SSH) and the path to the directory I'm in. That's what the <code>\h</code> and the <code>\w</code> are for. The Git information comes after that (if there is any) followed by a &gt;. I also like to make use of BASH's color support.
<pre class="example">function make-prompt
{
local RED="\[33[0;31m\]"
local GREEN="\[33[0;32m\]"
local LIGHT_GRAY="\[33[0;37m\]"
local CYAN="\[33[36m\]"
PS1="${CYAN}\h\
${GREEN} \w\
${RED} \$(gitify)\
${GREEN} &gt;\
${LIGHT_GRAY} "
}</pre>
</div>
</div>
<div id="outline-container-6" class="outline-3">
<h3 id="sec-6">Conclusion</h3>
<div id="text-6" class="outline-text-3">
I like this prompt because it gives me just enough information at a glance. I know where I am, if any changes have been made and how much I've diverged from the remote copy of my work. When I'm not in a Git repo the git information is gone. It's clean simple and informative.
I've borrowed heavily from both <a href="http://www.jonmaddox.com/2008/03/13/show-your-git-branch-name-in-your-prompt/">Jon Maddox</a> and <a href="https://github.com/holman/dotfiles/blob/master/zsh/prompt.zsh">Zach Holman</a> for some of the functionality. I didn't come across anyone showing the commit count, but I wouldn't be surprised if lots of other people have it too. There are probably other ways to get the same effect, this is just what I've found and settled on. The whole setup is available as a gist so feel free to use or fork it.
</div>
</div>

Show Git information in your prompt

I’ve been a sworn fan of version control for a good few years now. After a brief flirtation with Subversion I am currently in a long term and very committed relationship with the Git version control system. I use Git to store all my code and writing and to keep everything in sync between my machines. Almost everything I do goes into a repository.

When I’m working I spend most of my time in three applications: a text editor (generally Emacs), a terminal (either iTerm2 or Gnome Terminal) and a browser (Firefox or Safari). When in Emacs I use the excellent Magit mode to keep track of the status of my current project repository. However my interaction with git is generally split between Emacs and the terminal. There’s no real pattern, just what’s easiest and open at the moment. Unfortunately when I’m in the terminal there’s no visible cue as to what the status of the repo is. I have to be careful to run git status regularly to see what’s going. I need to manually make sure that I’ve committed everything and pushed to the remote server. Though this isn’t usually a problem, every now and then I’ll forget to commit and push something on one of my machines, go to another and then realized I’ve left behind all my work. It’s annoying and kills productivity.

Over the last few days I decided to sit down and give my terminal a regular indicator of the state of the current repository. So without further ado, here’s how I altered my Bash prompt to show relevant Git information.

Extracting Git information

There are generally three things I’m concerned about when it comes the Git repo I’m currently working on:

  1. What is the current branch I’m on?
  2. Are there any changes that haven’t been committed?
  3. Are there local commits that haven’t been pushed upstream?

Git provides a number of tools that gives you a lot of very detailed information about the state of the repo. Those tools are just a few commands away and I don’t want to be seeing everything there is to be seen at every step. I just want the minimum information to answer the above question.

Since the bash prompt is always visible (and updated after each command) I can put a small amount of text in the prompt to give me the information I want. In particular my prompt should show:

  1. The name of the current branch
  2. A “dirty” indicator if there are files that have been changed but not committed
  3. The number of local commits that haven’t been pushed

What is the current branch?

The symbolic-ref command shows the branch that the given reference points to. Since HEAD is the symbolic reference for the current state of the working tree, we can use git symbolic-ref HEAD to get the full branch. If we were on the master branch we would get back something like refs/heads/master. We use a little Awk magic to get rid of everything but the part after the last /. Wrapping this into a litte function we get:

function git-branch-name
{
    echo $(git symbolic-ref HEAD 2>/dev/null | awk -F/ {'print $NF'})
}

Has everything been committed?

Next we want to know if the branch is dirty, i.e. if there are uncommitted changes. The git status command gives us a detailed listing of the state of the repo. For our purposes is the very last line of the output. If there are no outstanding changes it says “nothing to commit (working directory clean)”. We can isolate the last line using the Unix tail utility and if it doesn’t match the above message we print a small asterisk (*). This is just enough to tell us that there is something we need to know about the repo and should run the full git status command.

Again, wrapping this all up into a little function we have:

function git-dirty {
    st=$(git status 2>/dev/null | tail -n 1)
    if [[ $st != "nothing to commit (working directory clean)" ]]
    then
        echo "*"
    fi
}

Have all commits been pushed?

Finally we want to know if all commits to the respective remote branch. We can use the git branch -v command to get a verbose listing of all the local branches. Since we already know the name of the branch we’re on, we use grep to isolate the line that tells us about our branch of interest. If we have local commits that haven’t been pushed the status line will say something like "[ahead X]", where X is the number of commits not pushed. We want to get that number.

Since what we’re looking for is a very well-defined pattern I decided to use BASH’s built-in regular expressions. I provide a pattern that matches =”[ahead X]” where X is a number. The matching number is stored in the BASH_REMATCH array. I can then print the number or nothing if no such match is present in the status line. The function we get is this:

function git-unpushed {
    brinfo=$(git branch -v | grep git-branch-name)
    if [[ $brinfo =~ ("[ahead "([[:digit:]]*)]) ]]
    then
        echo "(${BASH_REMATCH[2]})"
    fi
}

The =~ is the BASH regex match operator and the pattern used follows it.

Assembling the prompt

All that’s left is to tie together the functions and have them show up in the BASH prompt. I used a little function to check if the current directory is actually part of a repo. If the git status command only returns an error and nothing else then I’m not in a git repo and the functions I made would only give nonsense results. This functions checks the git status and then calls the other functions or does nothing.

function gitify {
    status=$(git status 2>/dev/null | tail -n 1)
    if [[ $status == "" ]]
    then
        echo ""
    else
        echo $(git-branch-name)$(git-dirty)$(git-unpushed)
    fi
}

Finally we could put together prompt. BASH allows for some common system information to be displayed in the prompt. I like to see the current hostname (to know which machine I’m on if I’m working over SSH) and the path to the directory I’m in. That’s what the \h and the \w are for. The Git information comes after that (if there is any) followed by a >. I also like to make use of BASH’s color support.

function make-prompt
{
    local         RED="\[\033[0;31m\]"
    local       GREEN="\[\033[0;32m\]"
    local  LIGHT_GRAY="\[\033[0;37m\]"
    local  CYAN="\[\033[36m\]"

    PS1="${CYAN}\h\
${GREEN} \w\
${RED} \$(gitify)\
${GREEN} >\
${LIGHT_GRAY} "

}

Conclusion

I like this prompt because it gives me just enough information at a glance. I know where I am, if any changes have been made and how much I’ve diverged from the remote copy of my work. When I’m not in a Git repo the git information is gone. It’s clean simple and informative.

I’ve borrowed heavily from both Jon Maddox and Zach Holman for some of the functionality. I didn’t come across anyone showing the commit count, but I wouldn’t be surprised if lots of other people have it too. There are probably other ways to get the same effect, this is just what I’ve found and settled on. The whole setup is available as a gist so feel free to use or fork it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment