add some boilerplate scripts
[hband-tools.git] / bash / git-prompt / git-prompt.sh
blob9e92eeb97872d0af63b4b9aef302cbde645f8a4e
1 #!/bin/bash
3 __git_prompt_print()
5 local n=0
6 local color
7 local line
8 local RESET
9 local gitdir prefix isthereworktree=''
10 local hash
11 local branch='' branches='' tags='' descbranch='' desctag='' pointer=''
12 local ahead='' behind='' divergence=''
13 local upstream='' fallback='' pushpull=''
15 local X Y
16 local MX='' AX='' DX='' RX='' CX='' UX='' QX='' EX=''
17 local MY='' AY='' DY='' RY='' CY='' UY='' QY='' EY=''
18 local AA='' DD='' AU='' DU='' UA='' UD='' UU='' U=''
19 local unstag unstag_tracked staged
20 local specdir='' speclist=() spec=''
22 local add del rest
23 local adds1='' dels1='' adds2='' dels2=''
24 local delta1 delta2
26 local sign_branch=
27 local sign_tags=
28 local sign_desc=
29 local sign_ahead=
30 local sign_behind=
31 local sign_unstag=
32 local sign_staged=
33 #local sign_stash=⌂ â�‚ âš‘ âš’ â—³ â—ª â—• â—� ⌘ ∗ â—� Ö â�– ðŸ�± ✪ â�Ÿ ⦿ â—•
34 local sign_stash=
35 local sign_pushpull=
36 local sign_origin=
37 local sign_clean=
38 #local sign_notes=� 🗠📋 � � 📎
39 local sign_notes=
41 trueish()
43 [ "${1,,}" = true -o "${1,,}" = yes -o "${1,,}" = y ] || [ "$1" -gt 0 ] 2>/dev/null
46 if ! trueish "$GIT_PROMPT_ENABLE"
47 then
48 return
51 get_tracking_branch()
53 # Options:
54 # -H fall back to "origin/HEAD" if it is tracked but current branch is not remotely tracked
55 # Arguments:
56 # - local branch name; mandatory
57 # - variable name to store remote tracking ref, or "origin/$1" if not found;
58 # it is being echoed if this argument is omitted
59 # - variable name to store "1" if branch is not remotely tracked, or "" otherwise
60 local okHEAD=''
61 if [ "$1" = -H ]
62 then
63 okHEAD=1
64 shift
66 local branch=$1
67 local savetovar=$2
68 local fellback=$3
69 local remote=`git config --get-all "branch.$branch.remote" | head -n1`
70 local rbranch
71 if [ -n "$remote" ]
72 then
73 rbranch=`git config --get-all "branch.$branch.merge" | head -n1`
74 # Strip "refs/heads/"
75 rbranch=${rbranch:11}
76 [ -n "$fellback" ] && declare -g $fellback=''
78 if [ -z "$remote" -o -z "$rbranch" ]
79 then
80 remote=origin
81 if [ $okHEAD ] && [ -n "$(git branch --remotes --list $remote/HEAD)" ]
82 then
83 rbranch=HEAD
84 else
85 rbranch=$branch
87 [ -n "$fellback" ] && declare -g $fellback=1
89 if [ -n "$savetovar" ]
90 then
91 declare -g $savetovar="$remote/$rbranch"
92 else
93 echo "$remote/$rbranch"
97 gitdir=`git rev-parse --git-dir 2>/dev/null` || return
98 hash=`git show -s --format=%H 2>/dev/null`
100 for color in BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE
102 local $color B$color ${color}BG
103 eval "$color='\e[0;3${n}m'"
104 eval "B$color='\e[1;3${n}m'"
105 eval "${color}BG='\e[4${n}m'"
106 let n++
107 done
108 RESET='\e[0m'
112 if [ -z "$hash" ]
113 then
114 speclist+=(INIT)
116 if [ "$(git rev-parse --is-bare-repository)" = true ]
117 then
118 specdir=BARE
120 if [ "$(git rev-parse --is-inside-git-dir)" = true ]
121 then
122 specdir=GITDIR
124 if [ -d "$GIT_WORK_TREE" ] || [ "$(git rev-parse --is-inside-work-tree)" = true ]
125 then
126 isthereworktree=1
130 IFS_=$IFS
131 IFS=$'\n'
132 branch=`git symbolic-ref --quiet --short HEAD`
133 if [ -n "$branch" ]
134 then
135 pushpull=$sign_pushpull
136 get_tracking_branch "$branch" upstream fallback
137 [ "$fallback" ] && pushpull=$sign_origin
139 line=`git rev-list --count --left-right "$branch...$upstream" 2>/dev/null`
140 # Empty $line means remote branch is not tracked.
141 # "0\t0" means they are in sync.
142 if [ "${line% *}" = 0 -a "${line#* }" = 0 ]
143 then
144 pushpull=''
146 else
147 desctag=`git describe --contains --tags HEAD 2>/dev/null`
148 descbranch=`git describe --contains --all HEAD 2>/dev/null`
149 descbranch=${descbranch#remotes/}
152 if [ -n "$hash" ]
153 then
154 for line in `git tag --list --points-at "$hash"`
156 tags=$tags${tags:+$CYAN,}$BCYAN$line
157 done
159 prefix="$hash refs/heads/"
160 for line in `git show-ref | grep "^$prefix"`
162 line=${line:${#prefix}}
163 [ "$line" != "$branch" ] && branches=$branches${branches:+$MAGENTA,}$BMAGENTA$line
164 done
167 for line in `test $isthereworktree && git status --porcelain ${GIT_PROMPT_SHOW_IGNORED:+--ignored}`
169 line=${line//\?/N}
170 line=${line//\!/E}
171 X=${line:0:1}
172 Y=${line:1:1}
173 case "$X$Y"
175 AA|DD)
176 let $X$Y++
178 [AD]U|U[ADU])
179 let U++
182 [ "$X" != ' ' ] && let ${X}X++
183 [ "$Y" != ' ' ] && let ${Y}Y++
185 esac
186 done
188 line=`git rev-list --count --left-right "$(git show-ref --quiet --verify refs/remotes/origin/HEAD && echo origin/HEAD || get_tracking_branch master)...HEAD" 2>/dev/null`
189 behind=${line% *}
190 ahead=${line#* }
192 if trueish "${GIT_PROMPT_COUNT_LINES-true}"
193 then
194 for line in `test $isthereworktree && git diff --numstat --find-copies=100`
196 line=${line% *}
197 line=${line//-/0}
198 let adds1+=${line% *}
199 let dels1+=${line#* }
200 done
202 for line in `git diff --numstat --staged --find-copies=100`
204 line=${line% *}
205 line=${line//-/0}
206 let adds2+=${line% *}
207 let dels2+=${line#* }
208 done
211 for var in ahead behind adds1 dels1 adds2 dels2
213 [ "${!var}" = 0 ] && eval $var=''
214 done
215 IFS=$IFS_
216 stash=$(git stash list -s --format=%H 2>/dev/null | wc -l)
217 notes=$(git notes list HEAD 2>/dev/null | wc -l)
220 if [ -d "$gitdir/rebase-merge" ]
221 then
222 branch=`cat "$gitdir/rebase-merge/head-name"`
223 if [ -f "$gitdir/rebase-merge/interactive" ]
224 then
225 speclist+=(REBASE-I)
226 else
227 speclist+=(REBASE-M)
230 if [ -d "$gitdir/rebase-apply" ]
231 then
232 if [ -f "$gitdir/rebase-apply/rebasing" ]
233 then
234 speclist+=(REBASE)
235 elif [ -f "$gitdir/rebase-apply/applying" ]
236 then
237 speclist+=(AM)
238 else
239 speclist+=(AM/REBASE)
242 [ -f "$gitdir/MERGE_HEAD" ] && speclist+=(MERGE)
243 [ -f "$gitdir/CHERRY_PICK_HEAD" ] && speclist+=(CHERRYPICK)
244 [ -f "$gitdir/BISECT_LOG" ] && speclist+=(BISECT)
247 ahash=$MAGENTA${hash:0:7}${hash:+ }
248 specdir=${specdir:+$BLACK$YELLOWBG$specdir$RESET }
249 for line in "${speclist[@]}"
251 spec="$spec${spec:+ }$BWHITE$REDBG$line$RESET"
252 done
253 if [ -z "$branch" ]
254 then
255 spec="$spec${spec:+ }$BWHITE${MAGENTABG}HEAD$RESET"
257 if [ -n "$branch$tags" ]
258 then
259 local branchesglue=''
260 if [ -n "$branches" ]
261 then
262 [ -n "$branch" ] && branchesglue="$MAGENTA," || branchesglue=" $BRED$sign_desc "
264 branches=${branch:+ $MAGENTA$sign_branch $BMAGENTA$branch}${pushpull:+$BYELLOW$pushpull}$branchesglue$branches
265 tags=${tags:+ $CYAN$sign_tags $tags}
266 pointer=$branches$tags
267 else
268 if [ -n "$desctag" ]
269 then
270 pointer=" $BRED$sign_desc $BCYAN$desctag"
271 elif [ -n "$descbranch" ]
272 then
273 pointer=" $BRED$sign_desc $BMAGENTA$descbranch"
276 ahead=${ahead:+$BBLUE$sign_ahead$RESET$ahead}
277 behind=${behind:+$BBLACK$sign_behind$RESET$behind}
278 divergence=$ahead$behind
280 if [ $stash = 0 ]
281 then
282 stash=''
283 elif [ $stash = 1 ]
284 then
285 stash="$BMAGENTA$sign_stash"
286 else
287 stash="$BMAGENTA$sign_stash$MAGENTA$stash"
290 if [ $notes = 0 ]
291 then
292 notes=''
293 else
294 notes=$BYELLOW$sign_notes
297 unstag=${NY:+ ${BCYAN}N$BWHITE$NY}${AY:+ ${BGREEN}A$BWHITE$AY}${DY:+ ${BRED}D$BWHITE$DY}${MY:+ ${BYELLOW}M$BWHITE$MY}${TY:+ ${BBLUE}T$RESET$TY}${CY:+ ${BGREEN}C$RESET$CY}${RY:+ ${BYELLOW}R$BWHITE$RY}${AA:+ ${BBLUE}A$BWHITE$AA}${DD:+ ${BBLUE}D$BWHITE$DD}${U:+ ${BYELLOW}${REDBG}U${RESET}$BWHITE$U}
298 staged=${AX:+ ${GREEN}A$RESET$AX}${DX:+ ${RED}D$RESET$DX}${MX:+ ${YELLOW}M$RESET$MX}${TX:+ ${BLUE}T$RESET$TX}${CX:+ ${GREEN}C$RESET$CX}${RX:+ ${YELLOW}R$RESET$RX}
299 unstag=${unstag:+$YELLOW$sign_unstag$unstag}
300 unstag_tracked=$AY$DY$MY$TY$CY$RY$AA$DD$U
301 staged=${staged:+$BWHITE$sign_staged$staged}
303 [ -n "$adds1$dels1" ] && delta1="${adds1:+$BGREEN+$adds1}${dels1:+$BRED-$dels1}$RESET" || delta1=''
304 [ -n "$adds2$dels2" ] && delta2="${adds2:+$GREEN+$adds2}${dels2:+$RED-$dels2}$RESET" || delta2=''
305 delta1=${delta1:+$BBLACK[$delta1$BBLACK]}
306 delta2=${delta2:+$BBLACK[$delta2$BBLACK]}
309 if [ -z "$unstag_tracked$staged" -a -n "$hash" ]
310 then
311 pointer="$pointer $GREEN$(git show -s --format=%cr)"
312 [ "$isthereworktree" = 1 -a -z "$pushpull" ] && pointer="$pointer${pointer:+ }$BGREEN$sign_clean"
316 __git_prompt="$ahash$specdir$spec$pointer${divergence:+ }$divergence${EY:+ ${BRED}!$BWHITE$EY}${unstag:+ }$unstag${delta1:+ }$delta1${stash:+ }$stash${staged:+ }$staged${delta2:+ }$delta2${notes:+ }$notes"
317 echo -n "\x01$__git_prompt$RESET\x02
318 \x01\x02"