11#! /bin/bash
22
3- # please direct comments, bug reports, feature requests, or PRs to the upstream repo:
4- # https://github.com/bill-auger/git-branch-status/issues/
5-
63# git-branch-status - print pretty git branch sync status reports
74#
85# Copyright 2014 Jehiah Czebotar <https://github.com/jehiah>
96# Copyright 2013 Fredrik Strandin <https://github.com/kd35a>
107# Copyright 2014 Kristijan Novoselić <https://github.com/knovoselic>
11- # Copyright 2014-2016 bill-auger <https://github.com/bill-auger>
8+ # Copyright 2014-2017 bill-auger <https://github.com/bill-auger>
129#
1310# git-branch-status is free software: you can redistribute it and/or modify
1411# it under the terms of the GNU General Public License version 3
2320# along with git-branch-status. If not, see <http://www.gnu.org/licenses/>.
2421
2522# credits:
26- # * originally by Jehiah Czebotar
23+ # * original `git rev-list` grepping by Jehiah Czebotar
2724# * "s'all good!" message by Fredrik Strandin
2825# * ANSI colors by Kristijan Novoselić
2926# * formatting, filters, usage, and remotes by bill-auger
3027
28+ # please direct comments, bug reports, feature requests, or PRs to one of the upstream repos:
29+ # * https://github.com/bill-auger/git-branch-status/issues/
30+ # * https://notabug.org/bill-auger/git-branch-status/issues/
31+
3132
32- read -r -d ' ' USAGE << -'USAGE '
33- usage :
33+ read -r -d ' ' USAGE << -'USAGE_MSG '
34+ USAGE :
3435
3536 git-branch-status
3637 git-branch-status [-a | --all]
@@ -40,15 +41,15 @@ usage:
4041 git-branch-status [-r | --remotes]
4142 git-branch-status [-v | --verbose]
4243
43- examples :
44+ EXAMPLES :
4445
45- # show only branches for which upstream HEAD differs from local
46+ # show only branches for which upstream differs from local
4647 $ git-branch-status
4748 | collab-branch | (behind 1) | (ahead 2) | origin/collab-branch |
4849 | feature-branch | (even) | (ahead 2) | origin/feature-branch |
4950 | master | (behind 1) | (even) | origin/master |
5051
51- # show all branches - even those with no upstream or no local and those up-to-date
52+ # show all branches - including those up-to-date, with no upstream, or not checked-out
5253 $ git-branch-status -a
5354 $ git-branch-status --all
5455 | master | (even) | (ahead 1) | origin/master |
@@ -79,24 +80,24 @@ examples:
7980 $ git-branch-status --help
8081 "prints this usage message"
8182
82- # show all remote branches - even those with no local
83+ # show all remote branches - including those not checked-out
8384 $ git-branch-status -r
8485 $ git-branch-status --remotes
85- | master | (behind 1) | (even) | a-remote/master |
86- | (no local) | n/a | n/a | a-remote/untracked-branch |
86+ | master | (behind 1) | (even) | a-remote/master |
87+ | (no local) | n/a | n/a | a-remote/untracked-branch |
8788
8889 # show all branches with timestamps (like -a -d)
8990 $ git-branch-status -v
9091 $ git-branch-status --verbose
9192 | 1999-12-31 local | n/a | n/a | (no upstream) |
9293 | 1999-12-31 master | (behind 1) | (even) | 2000-01-01 origin/master |
9394 | 1999-12-31 tracked | (even) | (even) | 2000-01-01 origin/tracked |
94- USAGE
95+ USAGE_MSG
9596
9697
9798# ## constants ###
9899
99- readonly MAX_COL_W=27 # should be => 12
100+ readonly MAX_COL_W=27 # should be >= 12
100101readonly CWHITE=' \033[0;37m'
101102readonly CGREEN=' \033[0;32m'
102103readonly CYELLOW=' \033[1;33m'
@@ -136,7 +137,7 @@ declare -a remote_colors=()
136137
137138# ## helpers ###
138139
139- function GetRefs # (a_refs_dir )
140+ function GetRefs # (refs_dir )
140141{
141142 git for-each-ref --format=" %(refname:short) %(upstream:short)" $1 2> /dev/null
142143}
@@ -146,60 +147,60 @@ function GetLocalRefs
146147 GetRefs refs/heads
147148}
148149
149- function GetRemoteRefs # (remote_repo )
150+ function GetRemoteRefs # (remote_repo_name )
150151{
151152 remote_repo=$1
152153
153154 GetRefs refs/remotes/$remote_repo
154155}
155156
156- function GetStatus
157+ function GetStatus # (local_commit remote_commit)
157158{
158- git rev-list --left-right ${local} ...${remote} -- 2> /dev/null
159+ local_commit=$1
160+ remote_commit=$2
161+
162+ git rev-list --left-right ${local_commit} ...${remote_commit} -- 2> /dev/null
159163}
160164
161165function GetCurrentBranch
162166{
163167 git rev-parse --abbrev-ref HEAD
164168}
165169
166- function IsCurrentBranch # (a_branch_name )
170+ function IsCurrentBranch # (branch_name )
167171{
172+ branch=$1
168173 current=$( GetCurrentBranch)
169- if (( $SHOW_DATES ))
170- then this_branch=$( GetHeadDate $1 ) $1 ; current=$( GetHeadDate $current ) $current ;
171- else this_branch=$1
172- fi
174+ this_branch=$( AppendHeadDate $branch ) ; current=$( AppendHeadDate $current ) ;
173175
174- if [ " $this_branch " == " $current " ] ; then echo 1 ; else echo 0 ; fi ;
176+ [ " $this_branch " == " $current " ] && echo 1 || echo 0
175177}
176178
177- function DoesBranchExist # (a_branch_name )
179+ function DoesBranchExist # (branch_name )
178180{
179- is_known_branch= $( git branch | grep -G " ^ $1 $ " ) # all but current
180- [ $( IsCurrentBranch $1 ) -o " $is_known_branch " ] && echo 1 || echo 0
181- }
181+ branch= $1
182+ is_current_branch= $( IsCurrentBranch $branch )
183+ is_known_branch= $( git branch | grep -G " ^ $branch $ " ) # all but current
182184
183- function SetFilterOrDie # (a_branch_name)
184- {
185- if (( $(DoesBranchExist $1 )) )
186- then branch=$1
187- else echo " no such branch: '$1 '" ; exit ;
188- fi
185+ (( $is_current_branch )) || [ " $is_known_branch " ] && echo 1 || echo 0
189186}
190187
191- function GetHeadDate # (a_commit_ref )
188+ function AppendHeadDate # (commit_ref )
192189{
193- author_date=$( git log -n 1 --format=format:" %ai" $1 2> /dev/null)
194- (( $SHOW_DATES )) && [ " $author_date " ] && echo " ${author_date: 0: 10} $JOIN_CHAR "
190+ commit=$1
191+ author_date=$( git log -n 1 --format=format:" %ai" $commit 2> /dev/null)
192+
193+ (( $SHOW_DATES )) && [ " $author_date " ] && date=" ${author_date: 0: 10} $JOIN_CHAR "
194+
195+ echo $date$commit
195196}
196197
197- function GetCommitMsg # (a_commit_ref )
198+ function GetCommitMsg # (commit_ref )
198199{
199200 git log -n 1 --format=format:" %s" $1
200201}
201202
202- function PrintHRule # (rule_w )
203+ function PrintHRule # (rule_width )
203204{
204205 printf " $( head -c $1 < /dev/zero | tr ' \0' $HRULE_CHAR ) \n"
205206}
@@ -224,59 +225,59 @@ function Reset
224225 remote_colors=()
225226}
226227
227- function GenerateReport # (a_local_branch_name a_remote_branch_name )
228+ function GenerateReport # (local_branch_name remote_branch_name )
228229{
229- local =$1
230- remote =$2
230+ local_branch =$1
231+ remote_branch =$2
231232 does_local_exist=$( DoesBranchExist $local_branch )
232233
233234 # filter branches per CLI arg
234- [ $branch ] && [ " $branch " != " $local " ] && return
235+ [ " $FILTER_BRANCH " ] && [ " $FILTER_BRANCH " != " $local_branch " ] && return
235236
236237 # filter heads
237- [ " $local " == " HEAD" ] && return
238+ [ " $local_branch " == ' HEAD' ] && return
238239
239240 # parse local<->remote sync status
240- if (( $does_local_exist )) && [ $remote ] ; then
241- status=$( GetStatus) ; (( $? )) && return ;
242-
243- n_behind=$( echo $status | tr " " " \n" | grep -c ' ^>' )
244- n_ahead=$( echo $status | tr " " " \n" | grep -c ' ^<' )
245- n_differences=$(( $n_behind + $n_ahead ))
246- n_total_differences=$(( $n_total_differences + $n_differences ))
247-
248- # filter branches by status
249- (( $SHOW_ALL_UPSTREAM )) || (( $n_differences )) || return
250-
251- # set data for branches with upstream
252- local_color=$CDEFAULT
253- if (( $n_behind ))
254- then behind_msg=" (behind$JOIN_CHAR$n_behind )" ; behind_color=$CBEHIND
255- else behind_msg=" (even)" ; behind_color=$CEVEN ;
256- fi
257- if (( $n_ahead ))
258- then ahead_msg=" (ahead$JOIN_CHAR$n_ahead )" ; ahead_color=$CAHEAD ;
259- else ahead_msg=" (even)" ; ahead_color=$CEVEN ;
260- fi
261- remote_color=$CDEFAULT
262- elif (( $does_local_exist )) && [ -z $remote ] && (( $SHOW_ALL_LOCAL )) ; then
263- # dummy data for local branches with no upstream counterpart
264- local_color=$CDEFAULT
265- behind_color=" $CDEFAULT " ; behind_msg=" n/a" ;
266- ahead_color=" $CDEFAULT " ; ahead_msg=" n/a" ;
267- remote_color=$CNOUPSTREAM ; remote =" $NO_UPSTREAM " ;
268- elif ! (( $does_local_exist )) && [ $remote ] && (( $SHOW_ALL_REMOTE )) ; then
269- # dummy data for remote branches with no local counterpart
270- local_color=$CNOLOCAL ; local =" $NO_LOCAL " ;
271- behind_color=" $CDEFAULT " ; behind_msg=" n/a" ;
272- ahead_color=" $CDEFAULT " ; ahead_msg=" n/a" ;
273- remote_color=$CDEFAULT
241+ if (( $does_local_exist )) && [ " $remote_branch " ]
242+ then status=$( GetStatus $local_branch $remote_branch ) ; (( $? )) && return ;
243+
244+ n_behind=$( echo $status | tr " " " \n" | grep -c ' ^>' )
245+ n_ahead=$( echo $status | tr " " " \n" | grep -c ' ^<' )
246+ n_differences=$(( $n_behind + $n_ahead ))
247+ n_total_differences=$(( $n_total_differences + $n_differences ))
248+
249+ # filter branches by status
250+ (( $SHOW_ALL_UPSTREAM )) || (( $n_differences )) || return
251+
252+ # set data for branches with upstream
253+ local_color=$CDEFAULT
254+ if (( $n_behind ))
255+ then behind_msg=" (behind$JOIN_CHAR$n_behind )" ; behind_color=$CBEHIND ;
256+ else behind_msg=" (even)" ; behind_color=$CEVEN ;
257+ fi
258+ if (( $n_ahead ))
259+ then ahead_msg=" (ahead$JOIN_CHAR$n_ahead )" ; ahead_color=$CAHEAD ;
260+ else ahead_msg=" (even)" ; ahead_color=$CEVEN ;
261+ fi
262+ remote_color=$CDEFAULT
263+ elif (( $does_local_exist )) && [ -z " $remote_branch " ] && (( $SHOW_ALL_LOCAL ))
264+ then # dummy data for local branches with no upstream counterpart
265+ local_color=$CDEFAULT
266+ behind_color=" $CDEFAULT " ; behind_msg=" n/a" ;
267+ ahead_color=" $CDEFAULT " ; ahead_msg=" n/a" ;
268+ remote_color=$CNOUPSTREAM ; remote_branch =" $NO_UPSTREAM " ;
269+ elif ! (( $does_local_exist )) && [ " $remote_branch " ] && (( $SHOW_ALL_REMOTE ))
270+ then # dummy data for remote branches with no local counterpart
271+ local_color=$CNOLOCAL ; local_branch =" $NO_LOCAL " ;
272+ behind_color=" $CDEFAULT " ; behind_msg=" n/a" ;
273+ ahead_color=" $CDEFAULT " ; ahead_msg=" n/a" ;
274+ remote_color=$CDEFAULT
274275 else return
275276 fi
276277
277278 # populate lists
278- local_msg=" $( GetHeadDate $local ) $local " ; local_msg=" ${local_msg: 0: $MAX_COL_W } " ;
279- remote_msg=" $( GetHeadDate $remote ) $remote " ; remote_msg=" ${remote_msg: 0: $MAX_COL_W } " ;
279+ local_msg=" $( AppendHeadDate $local_branch ) " ; local_msg=" ${local_msg: 0: $MAX_COL_W } " ;
280+ remote_msg=" $( AppendHeadDate $remote_branch ) " ; remote_msg=" ${remote_msg: 0: $MAX_COL_W } " ;
280281 local_msgs=( ${local_msgs[@]} " $local_msg " )
281282 behind_msgs=( ${behind_msgs[@]} " $behind_msg " )
282283 ahead_msgs=( ${ahead_msgs[@]} " $ahead_msg " )
@@ -287,9 +288,9 @@ function GenerateReport # (a_local_branch_name a_remote_branch_name)
287288 remote_colors=( ${remote_colors[@]} " $remote_color " )
288289
289290 # determine max column widths
290- if [ ${# local_msg} -gt $local_w ] ; then local_w=${# local_msg} ; fi ;
291+ if [ ${# local_msg} -gt $local_w ] ; then local_w=${# local_msg} ; fi ;
291292 if [ ${# behind_msg} -gt $behind_w ] ; then behind_w=${# behind_msg} ; fi ;
292- if [ ${# ahead_msg} -gt $ahead_w ] ; then ahead_w=${# ahead_msg} ; fi ;
293+ if [ ${# ahead_msg} -gt $ahead_w ] ; then ahead_w=${# ahead_msg} ; fi ;
293294 if [ ${# remote_msg} -gt $remote_w ] ; then remote_w=${# remote_msg} ; fi ;
294295}
295296
@@ -322,7 +323,7 @@ function PrintReportLine
322323 printf " $local_msg$behind_msg$ahead_msg$remote_msg$end_msg \n"
323324}
324325
325- function PrintReport # (header )
326+ function PrintReport # (table_header_line )
326327{
327328 header=$1
328329 n_notable_differences=${# local_msgs[@]}
@@ -353,28 +354,30 @@ function PrintReport # (header)
353354# ## main entry ###
354355
355356# parse CLI switches
356- if [ $1 ] ; then
357- show_dates=0
358- show_all=0
359- show_all_local=0
360- show_all_upstream=0
361- show_all_remote=0
362- if [ " $1 " == " -a" -o " $1 " == " --all" ] ; then show_all=1 ;
363- elif [ " $1 " == " -b" -o " $1 " == " --branch" ] ; then
364- if [ $2 ] ; then SetFilterOrDie $2 ; else branch=$( GetCurrentBranch) ; fi ;
365- elif [ " $1 " == " -d" -o " $1 " == " --dates" ] ; then show_dates=1 ;
366- elif [ " $1 " == " -h" -o " $1 " == " --help" ] ; then echo " $USAGE " ; exit ;
367- elif [ " $1 " == " -r" -o " $1 " == " --remotes" ] ; then show_all_remote=1 ;
368- elif [ " $1 " == " -v" -o " $1 " == " --verbose" ] ; then show_all=1 ;
369- show_dates=1 ;
370- else SetFilterOrDie $1
371- fi
372- readonly SHOW_DATES=$show_dates
373- readonly SHOW_ALL=$show_all
374- readonly SHOW_ALL_LOCAL=$(( $show_all + $show_all_local )) # also show branches that have no upstream
375- readonly SHOW_ALL_UPSTREAM=$(( $show_all + $show_all_upstream )) # also show branches that are up to date
376- readonly SHOW_ALL_REMOTE=$(( $show_all + $show_all_remote )) # also show branches that have no local
377- fi
357+ show_dates=0
358+ show_all=0
359+ show_all_local=0
360+ show_all_upstream=0
361+ show_all_remote=0
362+
363+ case " $1 " in
364+ ' -a' |' --all' ) show_all=1 ;;
365+ ' -b' |' --branch' ) [ " $2 " ] && branch=" $2 " || branch=$( GetCurrentBranch) ;;
366+ ' -d' |' --dates' ) show_dates=1 ;;
367+ ' -h' |' --help' ) echo " $USAGE " ; exit ;;
368+ ' -r' |' --remotes' ) show_all_remote=1 ;;
369+ ' -v' |' --verbose' ) show_all=1 ; show_dates=1 ;;
370+ * ) branch=" $1 " ;;
371+ esac
372+
373+ [ " $branch " ] && ! (( $(DoesBranchExist $branch )) ) && echo " no such branch: '$branch '" && exit
374+
375+ readonly FILTER_BRANCH=$branch
376+ readonly SHOW_DATES=$show_dates
377+ readonly SHOW_ALL=$show_all
378+ readonly SHOW_ALL_LOCAL=$(( $show_all + $show_all_local )) # also show branches that have no upstream
379+ readonly SHOW_ALL_UPSTREAM=$(( $show_all + $show_all_upstream )) # also show branches that are up to date
380+ readonly SHOW_ALL_REMOTE=$(( $show_all + $show_all_remote )) # also show branches that have no local
378381
379382
380383# compare local branches status to their upstreams
@@ -384,7 +387,6 @@ PrintReport "local <-> upstream"
384387
385388(( $SHOW_ALL_REMOTE )) || exit
386389
387-
388390# compare other remote branches status to local branches
389391for remote_repo in ` git remote`
390392do
0 commit comments