Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
99f24ed
trying to implement a preexec hook
niklas Jan 2, 2011
653fe2e
used wisdom by Glyph Lefkowitz to implement preexec hook for timer
niklas Jan 2, 2011
b030d9e
cool guys do not use the word function
niklas Jan 2, 2011
a17ca38
time of multiple commands is added
niklas Jan 2, 2011
27d1198
growlnotify support (untested)
niklas Jan 2, 2011
c6dd80e
human readable format of passed time while runing command
niklas Jan 2, 2011
2b75c5a
can define minimal amount of seconds for timer to show up
niklas Jan 2, 2011
f92d768
do not start notifers in background, finished message disrupts workflow
niklas Jan 2, 2011
b7195ac
can specify blacklist for commands not to be timed
niklas Jan 2, 2011
c61444e
.. and show the timer before putting the prompt
niklas Jan 2, 2011
b5cb6f2
enable setting the xterm_label also in TERM=gnome-256color
kronn Sep 6, 2011
1fa68c5
Merge pull request #1 from kronn/master
niklas Sep 8, 2011
4d81155
[git_module] only define a commandline completion for git if none exists
kronn Sep 13, 2011
be5aaf9
strip trailing whitespace
kronn Sep 13, 2011
7c7a3ab
Merge pull request #2 from kronn/master
niklas Oct 12, 2011
3b38ace
adapt parse_git_status to output of newer git-versions
kronn Oct 17, 2011
8756705
Merge pull request #3 from kronn/master
niklas Oct 22, 2011
1735900
move return-code retriecval to the top of the function
kronn Aug 3, 2012
232ad61
wrap return-code in brackets to separate it from the git-status
kronn Aug 3, 2012
75947ee
adapt to git 1.8 status output
kronn May 3, 2013
f3d4693
Merge pull request #6 from kronn/git-1-8-output
niklas Jul 1, 2013
67ed304
Backport upstream version of parse_git_status
kronn May 22, 2014
32d0500
Provide a backwards-compatible default for hex-length
kronn May 22, 2014
f658fa2
Backport more settings from upstream
kronn May 22, 2014
0154b79
Handle putting vim into the background and getting it back
kronn May 22, 2014
35bea98
Merge pull request #7 from kronn/git-1-9-output
niklas May 22, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion git-prompt.conf
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

# error_bell=off # sound terminal bell when command return code is not zero. (use setterm to set pitch and duration)
# max_file_list_length=100 # in characters

# count_only=off # off - display file list; on - display file count
# rawhex_len=5 # length of git rawhex revision id display (use 0 to hide it))

############################################################ MODULES

Expand Down
252 changes: 198 additions & 54 deletions git-prompt.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,13 @@

max_file_list_length=${max_file_list_length:-100}
upcase_hostname=${upcase_hostname:-on}
count_only=${count_only:-off}
rawhex_len=${rawhex_len:-5}


aj_max=20
timer_starts_at=10 # min seconds for timer
timer_blacklist="less*|vi*|man*|emacs*|fg*" # prefix-match on the command line


##################################################################### post config
Expand Down Expand Up @@ -190,7 +195,7 @@ cwd_truncate() {

# trunc middle if over limit
if [[ ${#path_middle} -gt $(( $cwd_middle_max + ${#elipses_marker} + 5 )) ]]; then

# truncate
middle_tail=${path_middle:${#path_middle}-${cwd_middle_max}}

Expand Down Expand Up @@ -227,7 +232,7 @@ set_shell_label() {
screen_label "$*"
;;

xterm* | rxvt* | gnome-terminal | konsole | eterm | wterm )
xterm* | rxvt* | gnome* | konsole | eterm | wterm )
# is there a capability which we can to test
# for "set term title-bar" and its escapes?
xterm_label "$plain_who_where $@"
Expand Down Expand Up @@ -261,7 +266,7 @@ set_shell_label() {

# we don't need tty name under X11
case $TERM in
xterm* | rxvt* | gnome-terminal | konsole | eterm | wterm ) unset tty ;;
xterm* | rxvt* | gnome* | konsole | eterm | wterm ) unset tty ;;
*);;
esac

Expand Down Expand Up @@ -389,7 +394,10 @@ parse_git_complete() {
return
fi

complete -f -W "$(
# only define a git completion if none exists
# the one provided by the system or git itself
# is more elaborate than this one.
complete -p git 1>/dev/null 2>&1 || complete -f -W "$(
echo `git branch -a | sed -e s/[\ \*]//g | cut -f 1 -d ' ' | uniq`; \
echo `git remote | sed -e s/[\ \*]//g | cut -f 1 -d ' ' | uniq`; \
echo `git | tail -23 | head -21 | cut -d ' ' -f 4`; \
Expand All @@ -411,46 +419,55 @@ parse_git_status() {
[[ -n ${git_dir/./} ]] || return 1

vcs=git
parse_git_complete

########################################################## GIT STATUS
file_regex='\([^/]*\/\{0,1\}\).*'
added_files=()
modified_files=()
untracked_files=()
added_files=()
modified_files=()
untracked_files=()
[[ $rawhex_len -gt 0 ]] && freshness="$dim="

unset branch status modified added clean init added mixed untracked op detached

# quoting hell
# work around for VTE bug (hang on printf)
unset VTE_VERSION

# info not in porcelain status
eval " $(
git status 2>/dev/null |
LANG=C git status 2>/dev/null |
sed -n '
s/^# On branch /branch=/p
s/^nothing to commit (working directory clean)/clean=clean/p
s/^# Initial commit/init=init/p

/^# Changes to be committed:/,/^# [A-Z]/ {
s/^# Changes to be committed:/added=added;/p

s/^# modified: '"$file_regex"'/ [[ \" ${added_files[*]} \" =~ \" \1 \" ]] || added_files[${#added_files[@]}]=\"\1\"/p
s/^# new file: '"$file_regex"'/ [[ \" ${added_files[*]} \" =~ \" \1 \" ]] || added_files[${#added_files[@]}]=\"\1\"/p
s/^# renamed:[^>]*> '"$file_regex"'/ [[ \" ${added_files[*]} \" =~ \" \1 \" ]] || added_files[${#added_files[@]}]=\"\1\"/p
s/^# copied:[^>]*> '"$file_regex"'/ [[ \" ${added_files[*]} \" =~ \" \1 \" ]] || added_files[${#added_files[@]}]=\"\1\"/p
}

/^# Changed but not updated:/,/^# [A-Z]/ {
s/^# Changed but not updated:/modified=modified;/p
s/^# modified: '"$file_regex"'/ [[ \" ${modified_files[*]} \" =~ \" \1 \" ]] || modified_files[${#modified_files[@]}]=\"\1\"/p
s/^# unmerged: '"$file_regex"'/ [[ \" ${modified_files[*]} \" =~ \" \1 \" ]] || modified_files[${#modified_files[@]}]=\"\1\"/p
}

/^# Untracked files:/,/^[^#]/{
s/^# Untracked files:/untracked=untracked;/p
s/^# '"$file_regex"'/ [[ \" ${untracked_files[*]} ${modified_files[*]} ${added_files[*]} \" =~ \" \1 \" ]] || untracked_files[${#untracked_files[@]}]=\"\1\"/p
}
s/^\(# \)*On branch /branch=/p
s/^nothing to commi.*/clean=clean/p
s/^\(# \)*Initial commi.*/init=init/p
s/^\(# \)*Your branch is ahead of \(.\).\+\1 by [[:digit:]]\+ commit.*/freshness=${WHITE}↑/p
s/^\(# \)*Your branch is behind \(.\).\+\1 by [[:digit:]]\+ commit.*/freshness=${YELLOW}↓/p
s/^\(# \)*Your branch and \(.\).\+\1 have diverged.*/freshness=${YELLOW}↕/p
'
)"

if ! grep -q "^ref:" $git_dir/HEAD 2>/dev/null; then
# porcelain file list
# TODO: sed-less -- http://tldp.org/LDP/abs/html/arrays.html -- Example 27-5

# git bug: (was reported to git@vger.kernel.org )
# echo 1 > "with space"
# git status --porcelain
# ?? with space <------------ NO QOUTES
# git add with\ space
# git status --porcelain
# A "with space" <------------- WITH QOUTES

eval " $(
LANG=C git status --porcelain 2>/dev/null |
sed -n '
s,^[MARC]. \([^\"][^/]*/\?\).*, added=added; [[ \" ${added_files[@]} \" =~ \" \1 \" ]] || added_files[${#added_files[@]}]=\"\1\",p
s,^[MARC]. \"\([^/]\+/\?\).*\"$, added=added; [[ \" ${added_files[@]} \" =~ \" \1 \" ]] || added_files[${#added_files[@]}]=\"\1\",p
s,^.[MAU] \([^\"][^/]*/\?\).*, modified=modified; [[ \" ${modified_files[@]} \" =~ \" \1 \" ]] || modified_files[${#modified_files[@]}]=\"\1\",p
s,^.[MAU] \"\([^/]\+/\?\).*\"$, modified=modified; [[ \" ${modified_files[@]} \" =~ \" \1 \" ]] || modified_files[${#modified_files[@]}]=\"\1\",p
s,^?? \([^\"][^/]*/\?\).*, untracked=untracked; [[ \" ${untracked_files[@]} \" =~ \" \1 \" ]] || untracked_files[${#untracked_files[@]}]=\"\1\",p
s,^?? \"\([^/]\+/\?\).*\"$, untracked=untracked; [[ \" ${untracked_files[@]} \" =~ \" \1 \" ]] || untracked_files[${#untracked_files[@]}]=\"\1\",p
' # |tee /dev/tty
)"

if ! grep -q "^ref:" "$git_dir/HEAD" 2>/dev/null; then
detached=detached
fi

Expand Down Expand Up @@ -497,20 +514,24 @@ parse_git_status() {


#### GET GIT HEX-REVISION
rawhex=`git rev-parse HEAD 2>/dev/null`
rawhex=${rawhex/HEAD/}
rawhex=${rawhex:0:6}
if [[ $rawhex_len -gt 0 ]] ; then
rawhex=`git rev-parse HEAD 2>/dev/null`
rawhex=${rawhex/HEAD/}
rawhex="$hex_vcs_color${rawhex:0:$rawhex_len}"
else
rawhex=""
fi

#### branch
branch=${branch/master/M}
branch=${branch/#master/M}

# another method of above:
# branch=$(git symbolic-ref -q HEAD || { echo -n "detached:" ; git name-rev --name-only HEAD 2>/dev/null; } )
# branch=${branch#refs/heads/}

### compose vcs_info

if [[ $init ]]; then
if [[ $init ]]; then
vcs_info=${white}init

else
Expand All @@ -525,7 +546,7 @@ parse_git_status() {
fi
#branch="<$branch>"
fi
vcs_info="$branch$white=$rawhex"
vcs_info="$branch$freshness$rawhex"

fi
}
Expand Down Expand Up @@ -581,9 +602,15 @@ parse_vcs_status() {

### file list
unset file_list
[[ ${added_files[0]} ]] && file_list+=" "$added_vcs_color${added_files[@]}
[[ ${modified_files[0]} ]] && file_list+=" "$modified_vcs_color${modified_files[@]}
[[ ${untracked_files[0]} ]] && file_list+=" "$untracked_vcs_color${untracked_files[@]}
if [[ $count_only = "on" ]] ; then
[[ ${added_files[0]} ]] && file_list+=" "${added_vcs_color}+${#added_files[@]}
[[ ${modified_files[0]} ]] && file_list+=" "${modified_vcs_color}*${#modified_files[@]}
[[ ${untracked_files[0]} ]] && file_list+=" "${untracked_vcs_color}?${#untracked_files[@]}
else
[[ ${added_files[0]} ]] && file_list+=" "$added_vcs_color${added_files[@]}
[[ ${modified_files[0]} ]] && file_list+=" "$modified_vcs_color${modified_files[@]}
[[ ${untracked_files[0]} ]] && file_list+=" "$untracked_vcs_color${untracked_files[@]}
fi
[[ ${vim_files} ]] && file_list+=" "${RED}vim:${vim_files}
file_list=${file_list:+:$file_list}

Expand All @@ -603,16 +630,63 @@ parse_vcs_status() {
#tail_local="${tail_local+$vcs_color $tail_local}${dir_color}"
}

disable_set_shell_label() {
# this is stolen from http://www.twistedmatrix.com/users/glyph/preexec.bash.txt
# do nothing we could do in the trap hook directly
preexec_invoke_exec () {
if [[ -z "$preexec_interactive_mode" ]]
then
# We're doing something related to displaying the prompt. Let the
# prompt set the title instead of me.
return
else
# If we're in a subshell, then the prompt won't be re-displayed to put
# us back into interactive mode, so let's not set the variable back.
# In other words, if you have a subshell like
# (sleep 1; sleep 2)
# You want to see the 'sleep 2' as a set_command_title as well.
if [[ 0 -eq "$BASH_SUBSHELL" ]]
then
preexec_interactive_mode=""
fi
fi
if [[ "prompt_command_function" == "$BASH_COMMAND" ]]
then
# Sadly, there's no cleaner way to detect two prompts being displayed
# one after another. This makes it important that PROMPT_COMMAND
# remain set _exactly_ as below in preexec_install. Let's switch back
# out of interactive mode and not trace any of the commands run in
# precmd.

# Given their buggy interaction between BASH_COMMAND and debug traps,
# versions of bash prior to 3.1 can't detect this at all.
preexec_interactive_mode=""
return
fi

# In more recent versions of bash, this could be set via the "BASH_COMMAND"
# variable, but using history here is better in some ways: for example, "ps
# auxf | less" will show up with both sides of the pipe if we use history,
# but only as "ps auxf" if not.
local this_command=`history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//g"`;

# If none of the previous checks have earlied out of this function, then
# the command is in fact interactive and we should invoke the user's
# preexec hook with the running command as an argument.
if [[ -n "$this_command" ]] ; then
preexec_command_function "$this_command"
fi
}

disable_preexec_command_function() {
trap - DEBUG >& /dev/null
}

# show currently executed command in label
enable_set_shell_label() {
disable_set_shell_label
# check for BASH_SOURCE being empty, no point running set_shell_label on every line of .bashrc
trap '[[ -z "$BASH_SOURCE" && ($BASH_COMMAND != prompt_command_function) ]] &&
set_shell_label $BASH_COMMAND' DEBUG >& /dev/null

# register a preexec hook eg to show currently executed command in label
enable_preexec_command_function() {
disable_preexec_command_function

trap '[[ -z "$COMP_LINE" ]] && preexec_invoke_exec' DEBUG >& /dev/null
}

# autojump (see http://wiki.github.com/joelthelion/autojump)
Expand All @@ -633,13 +707,17 @@ alias jumpstart='echo ${aj_dir_list[@]}'

###################################################################### PROMPT_COMMAND

preexec_interactive_mode=""
prompt_command_function() {
rc="$?"

timer_stop
preexec_interactive_mode="yes"

if [[ "$rc" == "0" ]]; then
rc=""
else
rc="$rc_color$rc$colors_reset$bell "
rc="$rc_color[$rc]$colors_reset$bell "
fi

cwd=${PWD/$HOME/\~} # substitute "~"
Expand All @@ -661,9 +739,75 @@ prompt_command_function() {
unset head_local tail_local pwd
}

PROMPT_COMMAND=prompt_command_function
timer_start() {
local command=$1
# unless applying the blacklist as prefix remove pattern results
# in and empty string (it matches)
if [ -n "${command##+(${TIMER_BLACKLIST:-timer_blacklist})}" ] ; then
export TIMER_COMMAND="$1"
export TIMER_STARTED_AT=$(date +'%s')
fi
}

timer_stop() {
if [ -z "$TIMER_COMMAND" ] ; then
return # no command to time
fi

local stopped_at=$(date +'%s')
local started_at=${TIMER_STARTED_AT:-$stopped_at}
let elapsed=$stopped_at-$started_at
local min=${TIMER_STARTS_AT:-${timer_starts_at:-10}}
if [ $elapsed -gt $min ]; then
timer_message
fi
export TIMER_COMMAND=""
}

timer_message() {
local stamper=''
if [ $elapsed -gt 60 ] ; then
if [ $elapsed -gt 3600 ] ; then
stamper="%-Hh, %-Mm, %-Ss"
else
stamper="%-Mm, %-Ss"
fi
else
stamper="%-S seconds"
fi
local human_elapsed=$(date -d "@$elapsed" +"$stamper")
local message="took $human_elapsed"

# decide which status to use
if [ "$?" == "0" ] ; then
result="completed"
else
result="FAILED ($status)"
fi
local title="$TIMER_COMMAND $result"

# TODO coloring
echo "$human_elapsed: $TIMER_COMMAND"

if type -P notify-send >&/dev/null ; then
notify-send -i terminal "$title" "$message"
fi
if type -P growlnotify >&/dev/null ; then
growlnotify -s "$title" -m "$message"
fi
}

preexec_command_function() {
local this_command="$1"

set_shell_label "$this_command"
timer_start "$this_command"
}


PROMPT_COMMAND="prompt_command_function"

enable_set_shell_label
enable_preexec_command_function

unset rc id tty modified_files file_list

Expand Down