1

This might be an odd problem, but I would like to override the behavior of ls when in a git directory.

I want to extract some diff statistics from the last commit, and append it to the bottom of the ls. When in the root of the git project, I want to make a specific prompt like this:

app/
test/
.gitignore
test.py
run.py
README.md

+ 11 lines
- 3 lines
Last commit: "add a new test for deployment" (Oct 14, 2014)

When deeper than the root, I want to create a prompt like this (say I cd'd to test/ and then use the ls command):

test/
├── __init__.py
├── test_deploy.py
└── test_add_user.py (+3 lines since last commit)

Bonus points: colorized green/red for plus/minus lines and nice uniform tabbing on git info.

How can this be achieved?

1

First, in order to create a custom ls, the easiest way is to proceed as follows:

  1. Create a directory $HOME/bin
  2. Add $HOME/bin to your PATH. To do this, open the file ~/.bashrc in your favorite text editor, and add the following line to the end:

    export PATH=$HOME/bin:$PATH
    

    Note that it is important that you prepend $HOME/bin to your PATH, so that executables in there will take precedence over other executables with the same name along your PATH (such as, in your case, the ls command)

  3. Create a file $HOME/bin/ls with the contents that I'll post below.
  4. Make it executable: chmod 755 $HOME/bin/ls.
  5. You will probably need to restart your terminal.

Second, your new $HOME/bin/ls should behave like so: It checks if the current directory is a git repository, and

  • if no, it simply delegates to the normal /bin/ls executable.
  • if yes, it creates that output you asked for, depending on whether the current folder is the root of the git repository or if it is deeper.

I quickly hacked such a script together for you. It's by no means perfect, but it should be pretty close to what you want, and you can easily work your way from there. :-)

#!/bin/bash

# determine if we're in a git repo, what the root dir is, and assign it to GIT_ROOT, all in one swoop
GIT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)

# if not in git repo, relay to normal ls
if (( $? )); then
  /bin/ls "$@"

# if in git repo, are we in root dir?
elif [ "x$GIT_ROOT" = "x$PWD" ]; then
  # first, normal ls with 1 file per line, also pass along other parameters
  /bin/ls -1 "$@"
  echo
  # a few git stats
  GIT_DIFF=$(git diff --stat | tail -1)
  # any insertions?
  echo $GIT_DIFF | grep insertion >/dev/null
  if (( ! $? )); then
    # bonus points: color in green
    echo -e "\e[0;32m+ $(echo $GIT_DIFF | sed 's/.*, \([[:digit:]]\+\) insertion.*/\1/;') lines\e[0m"
  fi
  # any deletions?
  echo $GIT_DIFF | grep deletion >/dev/null
  if (( ! $? )); then
    # bonus points: color in red
    echo -e "\e[0;31m- $(echo $GIT_DIFF | sed 's/.*, \([[:digit:]]\+\) deletion.*/\1/;') lines\e[0m"
  fi
  # Finally, display last commit message and date
  git log -1 --pretty='Last commit: "%s" (%ad)'

# otherwise, we're depper in a git repo
else
  echo -e "\e[1;34m${PWD##*/}\e[0m"
  IFS=$'\n'
  for file in $(tree -C | tail -n +2 | head -n -2); do
    filename=$(echo $file | cut -d" " -f 2-)
    echo -n $file
    # a few git stats
    GIT_DIFF=$(git diff --stat "$(echo $filename | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g")")
    # any insertions?
    echo $GIT_DIFF | grep insertion >/dev/null
    if (( ! $? )); then
      # bonus points: color in green
      echo -ne " \e[0;32m(+ $(echo $GIT_DIFF | sed 's/.*, \([[:digit:]]\+\) insertion.*/\1/;') lines since last commit)\e[0m"
    fi
    # any deletions?
    echo $GIT_DIFF | grep deletion >/dev/null
    if (( ! $? )); then
      # bonus points: color in red
      echo -ne " \e[0;31m(- $(echo $GIT_DIFF | sed 's/.*, \([[:digit:]]\+\) deletion.*/\1/;') lines since last commit)\e[0m"
    fi
    echo
  done

fi

Demo

Note: output is nicely colored, though clearly this won't show here.

$ ls
app
README.md
run.py
test
test.py

+ 11 lines
- 3 lines
Last commit: "add a new test for deployment" (Fri Nov 21 04:20:37 2014 +0100)
$ cd test/
$ ls
test
├── bla
├── __init__.py
├── test_add_user.py (+ 3 lines since last commit)
└── test_deploy.py

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.