Skip to content

Instantly share code, notes, and snippets.

@afazio
Created June 4, 2014 16:08
Show Gist options
  • Save afazio/81b68f60e9566d80da27 to your computer and use it in GitHub Desktop.
Save afazio/81b68f60e9566d80da27 to your computer and use it in GitHub Desktop.
ZSH prompt for Git and Ruby
## ~/.zshrc
# ...
# Load extensions from ~/.zsh/func/ directory
zgitinit
railsdir
# Setup basic prompt:
if [[ "$TERM" != "dumb" ]]; then
PROMPT="%B%m%b%# "
RPROMPT="[%h:%2.]"
else
PROMPT="%m%# "
fi
export BASE_PROMPT=$PROMPT
# Set precmd_functions to be a global array
typeset -ga precmd_functions
# Setup git prompt:
# Check git status of current directory and change prompt as necessary
git_precmd () {
local p=$BASE_PROMPT # strip last space off
[[ -n $NO_GIT || -n $NOGIT ]] && {
export PROMPT=$p
return 1
}
(zgit_isgit && zgit_issubdirtracked) || {
export PROMPT=$p
return 1
}
zgit_isworktreeclean || p="${p}${PR_RED}!${PR_NO_COLOR}"
zgit_hasuntracked && p="${p}${PR_RED}?${PR_NO_COLOR}"
zgit_isindexclean || p="${p}${PR_GREEN}+${PR_NO_COLOR}"
zgit_hasstash && p="${p}${PR_YELLOW}s${PR_NO_COLOR}"
zgit_isahead 2>/dev/null && p="${p}${PR_BLUE}>${PR_NO_COLOR}"
[[ $(zgit_branch) != "master" ]] && p="${p}${PR_BLUE}*${PR_NO_COLOR}"
if [[ $p != $BASE_PROMPT ]]; then
PROMPT="$p "
else
PROMPT="$BASE_PROMPT${PR_GREEN}=${PR_NO_COLOR} "
fi
export PROMPT
export GIT_PROMPT=$PROMPT
}
precmd_functions+='git_precmd'
# Setup railsdir extension
railsdir_precmd () {
local p=$BASE_PROMPT
if [[ -n $GIT_PROMPT ]]; then
p=$GIT_PROMPT
fi
is_rails_app_dir || {
export PROMPT=$p
return 1
}
get_rails_env >/dev/null
get_ruby_version >/dev/null
color=
case $RAILS_ENV in;
"local") color="";;
"test") color="";;
"development") color="${PR_YELLOW}";;
*) color="${PR_RED}";;
esac
p="${p}[${PR_GREEN}${RUBY_VERSION}${PR_NO_COLOR}:${color}$RAILS_ENV${PR_NO_COLOR}] "
PROMPT=$p
export PROMPT
}
precmd_functions+=railsdir_precmd
# -*- mode: sh -*-
## ~/.zsh/func/railsdir
typeset -ga __AJF_AVAILABLE_RAILS_ENVS
function is_rails_app_dir {
# If no directory provided, use pwd
[[ -z $1 ]] && 1=$(pwd)
# Does this look like a rails app?
if [[ -e "$1/Rakefile" && \
-d "$1/app" && \
-d "$1/config" && \
-f "$1/config/environment.rb" && \
-f "$1/config/routes.rb" ]]; then
# Set this variable to a list of available rails envs supported by
# this application.
if is_mac; then
__AJF_AVAILABLE_RAILS_ENVS=($(ls -1 "$1/config/environments/" | grep '\.rb$' | xargs basename | sed 's/\.rb//'))
else
__AJF_AVAILABLE_RAILS_ENVS=($(find config/environments -name '*.rb' -printf '%f\n' | sed 's/\.rb//'))
fi
export __AJF_AVAILABLE_RAILS_ENVS
# We are in a rails application. Return true
return 0
fi
## The current directory doesn't look like a rails application. Let's
## keep checking parent directories until we either find that we are in
## a rails application or we hit the root directory (/)
if [[ "$1" == "/" ]]; then
# We've made it to the root directory. exit
return 1 # Return false
else
# Otherwise, check the parent directory
is_rails_app_dir $1:h
return $?
fi
}
function get_rails_env {
typeset -g RAILS_ENV >/dev/null
[[ -z $RAILS_ENV ]] && {
if [[ ${__AJF_AVAILABLE_RAILS_ENVS[(i)local]} -le ${#__AJF_AVAILABLE_RAILS_ENVS} ]]; then
export RAILS_ENV=local
else
export RAILS_ENV=development
fi
}
echo -n $RAILS_ENV
}
function get_ruby_version {
typeset -g RUBY_VERSION >/dev/null
if which rvm >/dev/null; then
export RUBY_VERSION=$(rvm current | cut -d- -f 2)
else
export RUBY_VERSION=$(ruby --version | cut -d' ' -f 2)
fi
echo -n $RUBY_VERSION
}
# -*- sh -*-
## ~/.zsh/func/zgitinit
## Load with `autoload -U zgitinit; zgitinit'
##
typeset -gA zgit_info
zgit_info=()
zgit_chpwd_hook() {
zgit_info_update
}
zgit_preexec_hook() {
if [[ $2 == git\ * ]] || [[ $2 == *\ git\ * ]]; then
zgit_precmd_do_update=1
fi
}
zgit_precmd_hook() {
if [ $zgit_precmd_do_update ]; then
unset zgit_precmd_do_update
zgit_info_update
fi
}
zgit_info_update() {
zgit_info=()
local gitdir="$(git rev-parse --git-dir 2>/dev/null)"
if [[ $? -ne 0 ]] || [[ -z $gitdir ]] || [[ $gitdir == "." ]]; then
return
fi
zgit_info[dir]=$gitdir
zgit_info[bare]=$(git rev-parse --is-bare-repository)
}
zgit_isgit() {
if [[ -z "$zgit_info[dir]" ]]; then
return 1
else
return 0
fi
}
zgit_isbare() {
zgit_isgit || return
if [[ $zgit_info[bare] = "true" ]]; then
return 0
else
return 1
fi
}
zgit_head() {
zgit_isgit || return 1
if [[ -z $zgit_info[head] ]]; then
local name
name=$(git symbolic-ref -q HEAD)
if [[ $? -eq 0 ]]; then
if [[ $name == refs/(heads|tags)/* ]]; then
name=${name#refs/(heads|tags)/}
fi
else
name=$(git name-rev --name-only --no-undefined --always HEAD)
if [ $? -ne 0 ]; then
return 1
elif [[ $name == remotes/* ]]; then
name=${name#remotes/}
fi
fi
zgit_info[head]=$name
fi
echo $zgit_info[head]
}
zgit_isahead() {
zgit_isgit || return 1
ahead=$(git status | grep "Your branch is ahead of")
[[ -n $ahead ]] && return 0
return 1
}
zgit_branch() {
zgit_isgit || return 1
zgit_isbare && return 1
if [ -z "$zgit_info[branch]" ]; then
local branch=$(git symbolic-ref HEAD 2>/dev/null)
if [ $? -eq 0 ]; then
branch=${branch##*/}
else
branch=$(git name-rev --name-only --always HEAD)
fi
zgit_info[branch]=$branch
fi
echo $zgit_info[branch]
return 0
}
zgit_tracking_remote() {
zgit_isgit || return 1
zgit_isbare && return 1
local branch
if [ -n "$1" ]; then
branch=$1
elif [ -z "$zgit_info[branch]" ]; then
branch=$(zgit_branch)
[ $? -ne 0 ] && return 1
else
branch=$zgit_info[branch]
fi
local k="tracking_$branch"
local remote
if [ -z "$zgit_info[$k]" ]; then
remote=$(git config branch.$branch.remote)
zgit_info[$k]=$remote
fi
echo $zgit_info[$k]
return 0
}
zgit_tracking_merge() {
zgit_isgit || return 1
zgit_isbare && return 1
local branch
if [ -z "$zgit_info[branch]" ]; then
branch=$(zgit_branch)
[ $? -ne 0 ] && return 1
else
branch=$zgit_info[branch]
fi
local remote=$(zgit_tracking_remote $branch)
[ $? -ne 0 ] && return 1
if [ -n "$remote" ]; then # tracking branch
local merge=$(git config branch.$branch.merge)
if [ $remote != "." ]; then
merge=$remote/$(basename $merge)
fi
echo $merge
return 0
else
return 1
fi
}
zgit_isindexclean() {
zgit_isgit || return 1
if git diff --quiet --cached 2>/dev/null; then
return 0
else
return 1
fi
}
zgit_isworktreeclean() {
zgit_isgit || return 1
if git diff --quiet 2>/dev/null; then
return 0
else
return 1
fi
}
zgit_issubdirtracked() {
local d="$(git rev-parse --git-dir)"
local ds p="$(pwd)"
[[ $d == ".git" ]] && return 0;
ds=$(git ls-tree --name-only HEAD | head -n 1)
[[ -n $ds ]] && return 0;
d=$d:h
ds=$(cd $d; git ls-tree -r --full-name --name-only HEAD)
for directory in $ds; do
if [[ $ds == $p ]]; then
return 0;
fi
done
return 1;
}
zgit_hasuntracked() {
zgit_isgit || return 1
local -a flist
flist=($(git ls-files --others --exclude-standard))
if [ $#flist -gt 0 ]; then
return 0
else
return 1
fi
}
zgit_hasunmerged() {
zgit_isgit || return 1
local -a flist
flist=($(git ls-files -u))
if [ $#flist -gt 0 ]; then
return 0
else
return 1
fi
}
zgit_hasstash() {
zgit_isgit || return 1
local s
s=$(git stash list)
if [[ -n $s ]]; then
return 0;
fi
return 1;
}
zgit_svnhead() {
zgit_isgit || return 1
local commit=$1
if [ -z "$commit" ]; then
commit='HEAD'
fi
git show --raw $commit | \
grep git-svn-id | \
sed -re 's/^\s*git-svn-id: .*@([0-9]+).*$/\1/'
}
zgit_rebaseinfo() {
zgit_isgit || return 1
dotest=$zgit_info[dir]/.dotest-merge
if ! [ -d $dotest ]; then
if [ -d .dotest ]; then
dotest=.dotest
else
return 1
fi
fi
zgit_info[dotest]=$dotest
zgit_info[rb_onto]=$(cat "$dotest/onto")
if [ -f "$dotest/orig-head" ]; then
zgit_info[rb_head]=$(cat "$dotest/orig-head")
elif [ -f "$dotest/head" ]; then
zgit_info[rb_head]=$(cat "$dotest/head")
fi
zgit_info[rb_head_name]=$(cat "$dotest/head-name")
return 0
}
zgitinit() {
typeset -ga chpwd_functions
typeset -ga preexec_functions
typeset -ga precmd_functions
chpwd_functions+='zgit_chpwd_hook'
preexec_functions+='zgit_preexec_hook'
precmd_functions+='zgit_precmd_hook'
}
zgitinit
zgit_info_update
# vim:set ft=zsh:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment