22
33# git-branch-status - print pretty git branch sync status reports
44#
5- # Copyright 2014 Jehiah Czebotar <https://github.com/jehiah>
5+ # Copyright 2011 Jehiah Czebotar <https://github.com/jehiah>
66# Copyright 2013 Fredrik Strandin <https://github.com/kd35a>
77# Copyright 2014 Kristijan Novoselić <https://github.com/knovoselic>
88# Copyright 2014-2017 bill-auger <https://github.com/bill-auger>
2020# along with git-branch-status. If not, see <http://www.gnu.org/licenses/>.
2121
2222# credits:
23- # * original `git rev-list` grepping by Jehiah Czebotar
24- # * "s'all good!" message by Fredrik Strandin
25- # * ANSI colors by Kristijan Novoselić
26- # * formatting, filters, usage, and remotes by bill-auger
23+ # * original `git rev-list` grepping by Jehiah Czebotar
24+ # * "s'all good!" message by Fredrik Strandin
25+ # * ANSI colors by Kristijan Novoselić
26+ # * formatting, filters, switches,
27+ # usage, dates, remotes, and arbitrary branches by bill-auger
2728
2829# please direct comments, bug reports, feature requests, or PRs to one of the upstream repos:
2930# * https://github.com/bill-auger/git-branch-status/issues/
@@ -34,12 +35,14 @@ read -r -d '' USAGE <<-'USAGE_MSG'
3435USAGE:
3536
3637 git-branch-status
37- git-branch-status [-a | --all]
38- git-branch-status [-b | --branch] [branch-name]
39- git-branch-status [-d | --dates]
40- git-branch-status [-h | --help]
41- git-branch-status [-r | --remotes]
42- git-branch-status [-v | --verbose]
38+ git-branch-status [ base-branch-name compare-branch-name ]
39+ git-branch-status [ -a | --all ]
40+ git-branch-status [ -b | --branch ] [filter-branch-name]
41+ git-branch-status [ -d | --dates ]
42+ git-branch-status [ -h | --help ]
43+ git-branch-status [ -r | --remotes ]
44+ git-branch-status [ -v | --verbose ]
45+
4346
4447EXAMPLES:
4548
@@ -49,6 +52,12 @@ EXAMPLES:
4952 | feature-branch | (even) | (ahead 2) | origin/feature-branch |
5053 | master | (behind 1) | (even) | origin/master |
5154
55+ # compare two arbitrary branches (either local and either remote)
56+ $ git-branch-status local-arbitrary-branch fork/arbitrary-branch
57+ | local-arbitrary-branch | (even) | (ahead 1) | fork/arbitrary-branch |
58+ $ git-branch-status fork/arbitrary-branch local-arbitrary-branch
59+ | fork/arbitrary-branch | (behind 1) | (even) | local-arbitrary-branch |
60+
5261 # show all branches - including those up-to-date, with no upstream, or not checked-out
5362 $ git-branch-status -a
5463 $ git-branch-status --all
@@ -118,11 +127,15 @@ readonly STAR='*'
118127readonly DELIM=' |'
119128readonly NO_UPSTREAM=" (no${JOIN_CHAR} upstream)"
120129readonly NO_LOCAL=" (no${JOIN_CHAR} local)"
121- readonly NO_RESULTS_MSG=" Everything is synchronized"
130+ readonly LOCALS_SYNCED_MSG=" All tracking branches are synchronized with their upstreams"
131+ readonly BRANCH_SYNCED_MSG=" This tracking branch is synchronized with it's upstream"
132+ readonly REMOTES_SYNCED_MSG=" All remote branches with identically named local branches are synchronized with them"
133+ readonly UNTRACKED_SYNCHED_MSG=" These branches are synchronized"
122134
123135
124136# ## variables ###
125- n_total_differences=0
137+ n_tracked_differences=0
138+ were_any_branches_compared=0
126139local_w=0
127140behind_w=0
128141ahead_w=0
@@ -181,19 +194,34 @@ function GetUpstreamBranch # (local_branch)
181194function IsCurrentBranch # (branch_name)
182195{
183196 branch=$1
184- current=$( GetCurrentBranch)
185- this_branch=$( AppendHeadDate $branch ) ; current=$( AppendHeadDate $current ) ;
197+ this_branch=$( AppendHeadDate $branch )
198+ current_branch=$( AppendHeadDate $( GetCurrentBranch) )
199+
200+ [ " $this_branch " == " $current_branch " ] && echo 1 || echo 0
201+ }
202+
203+ function IsLocalBranch # (branch_name)
204+ {
205+ branch=$1
206+ is_local_branch=$( git branch -a | grep -P " ^(?:\*| ) $branch $" )
186207
187- [ " $this_branch " == " $current " ] && echo 1 || echo 0
208+ [ " $is_local_branch " ] && echo 1 || echo 0
209+ }
210+
211+ function IsTrackedBranch # (base_branch_name compare_branch_name)
212+ {
213+ base_branch=$1
214+ compare_branch=$2
215+ upstream_branch=$( GetUpstreamBranch $base_branch )
216+ [ " $compare_branch " == " $upstream_branch " ] && echo 1 || echo 0
188217}
189218
190219function DoesBranchExist # (branch_name)
191220{
192221 branch=$1
193- is_current_branch=$( IsCurrentBranch $branch )
194- is_known_branch=$( git branch | grep -G " ^ $branch $" ) # all but current
222+ is_known_branch=$( git branch -a | grep -P " ^(?:\*| ) (remotes\/)?$branch $" )
195223
196- (( $is_current_branch )) || [ " $is_known_branch " ] && echo 1 || echo 0
224+ [ " $is_known_branch " ] && echo 1 || echo 0
197225}
198226
199227function AppendHeadDate # (commit_ref)
@@ -221,7 +249,8 @@ function PrintHRule # (rule_width)
221249
222250function Reset
223251{
224- n_total_differences=0
252+ n_tracked_differences=0
253+ were_any_branches_compared=0
225254 local_w=0
226255 behind_w=0
227256 ahead_w=0
@@ -238,31 +267,33 @@ function Reset
238267
239268function GenerateReport # (local_branch_name remote_branch_name)
240269{
241- local_branch=$1
242- remote_branch=$2
243- upstream_branch=$( GetUpstreamBranch $local_branch )
244- does_local_exist=$( DoesBranchExist $local_branch )
245-
246- # filter branches per CLI arg
247- [ " $FILTER_BRANCH " ] && [ " $FILTER_BRANCH " != " $local_branch " ] && return
270+ base_branch=$1
271+ compare_branch=$2
272+ does_base_branch_exist=$( DoesBranchExist $base_branch )
273+ does_compare_branch_exist=$( DoesBranchExist $compare_branch )
274+ is_tracked_branch=$( IsTrackedBranch $base_branch $compare_branch )
248275
249276 # filter heads
250- [ " $local_branch " == ' HEAD' ] && return
277+ [ " $base_branch " == ' HEAD' ] && return
278+
279+ # filter branches per CLI arg
280+ [ " $FILTER_BRANCH " -a " $base_branch " != " $FILTER_BRANCH " ] && return
251281
252- # parse local<->remote sync status
253- if (( $does_local_exist )) && [ " $remote_branch " ]
254- then status=$( GetStatus $local_branch $remote_branch ) ; (( $? )) && return ;
282+ # parse local<->remote or arbitrary branches sync status
283+ if (( $does_base_branch_exist )) && (( $does_compare_branch_exist ))
284+ then status=$( GetStatus $base_branch $compare_branch ) ; (( $? )) && return ;
255285
256286 n_behind=$( echo $status | tr " " " \n" | grep -c ' ^>' )
257287 n_ahead=$( echo $status | tr " " " \n" | grep -c ' ^<' )
258288 n_differences=$(( $n_behind + $n_ahead ))
259- n_total_differences=$(( $n_total_differences + $n_differences ))
289+ n_tracked_differences=$(( $n_tracked_differences + $n_differences ))
290+ were_any_branches_compared=1
260291
261292 # filter branches by status
262293 (( $SHOW_ALL_UPSTREAM )) || (( $n_differences )) || return
263294
264- # set data for branches with remote counterparts
265- [ " $remote_branch " == " $upstream_branch " ] && color=$CTRACKING || color=$CDEFAULT
295+ # set data for branches with remote counterparts or arbitrary branches
296+ (( $is_tracked_branch )) && color=$CTRACKING || color=$CDEFAULT
266297 local_color=$color
267298 if (( $n_behind ))
268299 then behind_msg=" (behind$JOIN_CHAR$n_behind )" ; behind_color=$CBEHIND ;
@@ -273,24 +304,24 @@ function GenerateReport # (local_branch_name remote_branch_name)
273304 else ahead_msg=" (even)" ; ahead_color=$CEVEN ;
274305 fi
275306 remote_color=$color
276- elif (( $does_local_exist )) && [ -z " $remote_branch " ] && (( $SHOW_ALL_LOCAL ))
307+ elif (( $does_base_branch_exist )) && ! (( $does_compare_branch_exist )) && (( $SHOW_ALL_LOCAL ))
277308 then # dummy data for local branches with no upstream counterpart
278309 local_color=$CDEFAULT
279- behind_color=" $CDEFAULT " ; behind_msg=" n/a" ;
280- ahead_color=" $CDEFAULT " ; ahead_msg=" n/a" ;
281- remote_color=$CNOUPSTREAM ; remote_branch =" $NO_UPSTREAM " ;
282- elif ! (( $does_local_exist )) && [ " $remote_branch " ] && (( $SHOW_ALL_REMOTE ))
310+ behind_color=" $CDEFAULT " ; behind_msg=" n/a" ;
311+ ahead_color=" $CDEFAULT " ; ahead_msg=" n/a" ;
312+ remote_color=$CNOUPSTREAM ; compare_branch =" $NO_UPSTREAM " ;
313+ elif ! (( $does_base_branch_exist )) && (( $does_compare_branch_exist )) && (( $SHOW_ALL_REMOTE ))
283314 then # dummy data for remote branches with no local counterpart
284- local_color=$CNOLOCAL ; local_branch =" $NO_LOCAL " ;
285- behind_color=" $CDEFAULT " ; behind_msg=" n/a" ;
286- ahead_color=" $CDEFAULT " ; ahead_msg=" n/a" ;
315+ local_color=$CNOLOCAL ; base_branch =" $NO_LOCAL " ;
316+ behind_color=" $CDEFAULT " ; behind_msg=" n/a" ;
317+ ahead_color=" $CDEFAULT " ; ahead_msg=" n/a" ;
287318 remote_color=$CDEFAULT
288319 else return
289320 fi
290321
291322 # populate lists
292- local_msg=" $( AppendHeadDate $local_branch ) " ; local_msg=" ${local_msg: 0: $MAX_COL_W } " ;
293- remote_msg=" $( AppendHeadDate $remote_branch ) " ; remote_msg=" ${remote_msg: 0: $MAX_COL_W } " ;
323+ local_msg=" $( AppendHeadDate $base_branch ) " ; local_msg=" ${local_msg: 0: $MAX_COL_W } " ;
324+ remote_msg=" $( AppendHeadDate $compare_branch ) " ; remote_msg=" ${remote_msg: 0: $MAX_COL_W } " ;
294325 local_msgs=( ${local_msgs[@]} " $local_msg " )
295326 behind_msgs=( ${behind_msgs[@]} " $behind_msg " )
296327 ahead_msgs=( ${ahead_msgs[@]} " $ahead_msg " )
@@ -336,27 +367,33 @@ function PrintReportLine
336367 printf " $local_msg$behind_msg$ahead_msg$remote_msg$end_msg \n"
337368}
338369
339- function PrintReport # (table_header_line)
370+ function PrintReport # (table_header_line no_results_msg )
340371{
341372 header=$1
373+ no_results_msg=$2
342374 n_notable_differences=${# local_msgs[@]}
375+ (( $were_any_branches_compared )) && ! (( $n_tracked_differences )) && all_in_sync=1 || all_in_sync=0
376+
377+ ! (( $n_notable_differences )) && ! (( $all_in_sync )) && return
343378
344379 # pretty print results
345380 printf " \n $header \n"
346- if [ " $n_notable_differences " != " 0 " ]
381+ if (( $n_notable_differences ))
347382 then rule_w=$(( $local_w + $behind_w + $ahead_w + $remote_w + 13 ))
383+
348384 PrintHRule $rule_w
349385 for (( result_n = 0 ; result_n < $n_notable_differences ; result_n++ ))
350386 do PrintReportLine
351387 done
352388 PrintHRule $rule_w
353389 fi
354390
355- # print something if no diffs
356- if [ " $n_total_differences " == " 0" -a " $( GetRefs) " ]
357- then rule_w=$(( ${# NO_RESULTS_MSG} + 4 ))
391+ # print synchronized message if all compared upstreams had no diffs
392+ if (( $all_in_sync ))
393+ then rule_w=$(( ${# no_results_msg} + 4 ))
394+
358395 PrintHRule $rule_w
359- echo -e " $DELIM $CEVEN$NO_RESULTS_MSG $CEND $DELIM "
396+ echo -e " $DELIM $CEVEN$no_results_msg $CEND $DELIM "
360397 PrintHRule $rule_w
361398 fi
362399
@@ -374,40 +411,53 @@ show_all_upstream=0
374411show_all_remote=0
375412
376413case " $1 " in
377- ' -a' |' --all' ) show_all=1 ;;
378- ' -b' |' --branch' ) [ " $2 " ] && branch =" $2 " || branch =$( GetCurrentBranch) ;;
379- ' -d' |' --dates' ) show_dates=1 ;;
380- ' -h' |' --help' ) echo " $USAGE " ; exit ;;
381- ' -r' |' --remotes' ) show_all_remote=1 ;;
382- ' -v' |' --verbose' ) show_all=1 ; show_dates=1 ;;
383- * ) branch =" $1 " ;;
414+ ' -a' |' --all' ) show_all=1 ;;
415+ ' -b' |' --branch' ) [ " $2 " ] && branch_a =" $2 " || branch_a =$( GetCurrentBranch) ;;
416+ ' -d' |' --dates' ) show_dates=1 ;;
417+ ' -h' |' --help' ) echo " $USAGE " ; exit ;;
418+ ' -r' |' --remotes' ) show_all_remote=1 ;;
419+ ' -v' |' --verbose' ) show_all=1 ; show_dates=1 ;;
420+ * ) branch_a =" $1 " branch_b= " $2 " ;;
384421esac
385422
386- [ " $branch " ] && ! (( $(DoesBranchExist $branch )) ) && echo " no such branch: '$branch '" && exit
423+ [ " $branch_a " ] && ! (( $(DoesBranchExist $branch_a )) ) && echo " no such branch: '$branch_a '" && exit
424+ [ " $branch_b " ] && ! (( $(DoesBranchExist $branch_b )) ) && echo " no such branch: '$branch_b '" && exit
425+ [ " $branch_a " ] && [ -z " $branch_b " ] && ! (( $(IsLocalBranch $branch_a )) ) && echo " no such local branch: '$branch_a '" && exit
426+ [ " $branch_a " ] && show_all_local=1 # force "no upstream" message for non-tracking branches
387427
388- readonly FILTER_BRANCH=$branch
428+ readonly FILTER_BRANCH=$branch_a
429+ readonly COMPARE_BRANCH=$branch_b
389430readonly SHOW_DATES=$show_dates
390431readonly SHOW_ALL=$show_all
391432readonly SHOW_ALL_LOCAL=$(( $show_all + $show_all_local )) # show local branches that are not tracking any upstream
392433readonly SHOW_ALL_UPSTREAM=$(( $show_all + $show_all_upstream )) # show local branches that are synchronized with their upstream
393434readonly SHOW_ALL_REMOTE=$(( $show_all + $show_all_remote )) # show all remote branches
394435
395436
396- # compare local branches status to their upstreams
397- while read local upstream ; do GenerateReport $local $upstream ; done < <( GetLocalRefs) ;
398- PrintReport " local <-> upstream"
437+ if [ -z " $COMPARE_BRANCH " ]
438+ then [ " $FILTER_BRANCH " ] && synched_msg=" $BRANCH_SYNCED_MSG " || synched_msg=" $LOCALS_SYNCED_MSG "
439+
440+ # compare sync status of local branches to their upstreams
441+ while read local upstream ; do GenerateReport $local $upstream ; done < <( GetLocalRefs) ;
442+ PrintReport " local <-> upstream" " $synched_msg "
443+ else is_tracked_branch=$( IsTrackedBranch $FILTER_BRANCH $COMPARE_BRANCH )
444+ (( $is_tracked_branch )) && synched_msg=" $BRANCH_SYNCED_MSG " || synched_msg=" $UNTRACKED_SYNCHED_MSG "
445+
446+ # compare sync status of arbitrary branches per cli args
447+ GenerateReport $FILTER_BRANCH $COMPARE_BRANCH
448+ PrintReport " $FILTER_BRANCH <-> $COMPARE_BRANCH " " $synched_msg "
449+ fi
399450
400451
401- (( $SHOW_ALL_REMOTE )) || exit
452+ (( $SHOW_ALL_REMOTE )) && [ -z " $FILTER_BRANCH " ] || exit
402453
403- # compare other remote branches status to local branches
454+ # compare sync status of remote branches to local branches with the same names
404455for remote_repo in ` git remote`
405- do
406- while read remote_branch
407- do local_branch=${remote_branch# $remote_repo / }
456+ do while read remote_branch
457+ do local_branch=${remote_branch# $remote_repo / }
408458
409- GenerateReport $local_branch $remote_branch
410- done < <( GetRemoteRefs $remote_repo )
459+ GenerateReport $local_branch $remote_branch
460+ done < <( GetRemoteRefs $remote_repo )
411461
412- PrintReport " local <-> $remote_repo "
462+ PrintReport " local <-> $remote_repo " " $REMOTES_SYNCED_MSG "
413463done
0 commit comments