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- # * various features and maintenance - 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+ # * various features and maintenance - by bill-auger
2727
28- # please direct comments, bug reports, feature requests, or PRs to one of the upstream repos:
28+ # please direct bug reports, feature requests, or PRs to one of the upstream repos:
2929# * https://github.com/bill-auger/git-branch-status/issues/
3030# * https://notabug.org/bill-auger/git-branch-status/issues/
3131
@@ -111,11 +111,13 @@ EXAMPLES:
111111USAGE_MSG
112112
113113
114- # ## constants # ##
114+ # # constants ##
115115
116116readonly INNER_PADDING_W=13 # '| ' + ' | ' + ' | ' + ' | ' + ' |'
117117readonly MARGIN_PAD_W=2
118- readonly ALL_PADDING_W=$(( $INNER_PADDING_W + ($MARGIN_PAD_W * 2 )) )
118+ readonly MARGINS_PAD_W=$(( $MARGIN_PAD_W * 2 ))
119+ readonly ALL_PADDING_W=$(( $INNER_PADDING_W + $MARGINS_PAD_W ))
120+ readonly MIN_TTY_W=60 # ASSERT: (0 < $ALL_PADDING_W << $MIN_TTY_W)
119121readonly CWHITE=' \033[0;37m'
120122readonly CRED=' \033[0;31m'
121123readonly CGREEN=' \033[0;32m'
@@ -131,18 +133,19 @@ readonly CNOUPSTREAM=$CRED
131133readonly CNOLOCAL=$CRED
132134readonly HRULE_CHAR=' -'
133135readonly JOIN_CHAR=' ~'
134- readonly JOIN_REGEX=" s/$JOIN_CHAR / /"
136+ readonly JOIN_REGEX=" s/$JOIN_CHAR / /g "
135137readonly STAR=' *'
136138readonly DELIM=' |'
137139readonly NO_UPSTREAM=" (no${JOIN_CHAR} upstream)"
138140readonly NO_LOCAL=" (no${JOIN_CHAR} local)"
139141readonly LOCALS_SYNCED_MSG=" All tracking branches are synchronized with their upstreams"
140142readonly BRANCH_SYNCED_MSG=" This tracking branch is synchronized with it's upstream"
141- readonly REMOTES_SYNCED_MSG= " All identically named remote branches are synchronized"
142- readonly UNTRACKED_SYNCHED_MSG= " These branches are synchronized"
143+ readonly UNTRACKED_SYNCHED_MSG= " These branches are synchronized with no tracking relationship "
144+ readonly REMOTES_SYNCED_MSG= " All local branches with identical names to those on this remote are synchronized with this remote "
143145
144146
145- # ## variables ###
147+ # # variables ##
148+
146149n_tracked_differences=0
147150were_any_branches_compared=0
148151local_w=0
@@ -159,7 +162,7 @@ declare -a ahead_colors=()
159162declare -a remote_colors=()
160163
161164
162- # ## helpers # ##
165+ # # helpers ##
163166
164167function GetRefs # (refs_dir)
165168{
@@ -222,6 +225,7 @@ function IsTrackedBranch # (base_branch_name compare_branch_name)
222225 base_branch=$1
223226 compare_branch=$2
224227 upstream_branch=$( GetUpstreamBranch $base_branch )
228+
225229 [ " $compare_branch " == " $upstream_branch " ] && echo 1 || echo 0
226230}
227231
@@ -243,18 +247,30 @@ function AppendHeadDate # (commit_ref)
243247 echo $date$commit
244248}
245249
246- function GetCommitMsg # (commit_ref)
250+ # function GetCommitMsg # (commit_ref)
251+ # {
252+ # git log -n 1 --format=format:"%s" $1
253+ # }
254+
255+ function CurrentTtyW
247256{
248- git log -n 1 --format=format: " %s " $1
257+ stty -F /dev/tty size | cut -d ' ' -f 2
249258}
250259
251260function PrintHRule # (rule_width)
252261{
253262 printf " %$(( $MARGIN_PAD_W )) s$( head -c $1 < /dev/zero | tr ' \0' $HRULE_CHAR ) \n"
254263}
255264
265+ function EXIT # (err_msg)
266+ {
267+ err_msg=$1
268+
269+ echo " $err_msg " && exit
270+ }
271+
256272
257- # ## business # ##
273+ # # business ##
258274
259275function Reset
260276{
@@ -347,45 +363,54 @@ function GenerateReport # (local_branch_name remote_branch_name)
347363 if [ ${# remote_msg} -gt $remote_w ] ; then remote_w=${# remote_msg} ; fi ;
348364}
349365
350- function PrintReport # (table_header_line no_results_msg )
366+ function PrintReport # (table_header_line synchronized_msg )
351367{
352368 header=$1
353- no_results_msg=$2
369+ synchronized_msg=$2
370+ available_w=$(( $(CurrentTtyW) - $ahead_w - $behind_w - $ALL_PADDING_W ))
354371 n_notable_differences=${# local_msgs[@]}
355372 (( $were_any_branches_compared )) && ! (( $n_tracked_differences )) && all_in_sync=1 || all_in_sync=0
356373
357- ! (( $n_notable_differences )) && ! (( $all_in_sync )) && return
374+ (( $available_w < 0 )) && return # should never happen
375+ ! (( $n_notable_differences )) && ! (( $all_in_sync )) && return # should never happen
358376
359377 # truncate column widths to fit
360- current_tty_w=$(( $(stty - F / dev/ tty size | cut - d ' ' - f2 )) )
361- available_w=$(( $(($current_tty_w - $ahead_w - $behind_w - $ALL_PADDING_W )) ))
362- while (( $local_w + $remote_w > $available_w ))
363- do if (( $local_w > $remote_w ))
364- then local_w=$(( $local_w - 1 ))
365- elif (( $remote_w > $local_w ))
366- then remote_w=$(( $remote_w - 1 ))
367- else local_w=$(( $local_w - 1 )) ; remote_w=$(( $remote_w - 1 )) ;
368- fi
378+ while (( $local_w + $remote_w > $available_w ))
379+ do (( $local_w >= $remote_w )) && local_w=$(( $local_w - 1 ))
380+ (( $local_w <= $remote_w )) && remote_w=$(( $remote_w - 1 ))
369381 done
370382
371- # pretty print results
383+ # pretty print divergence results
372384 printf " \n $header \n"
373385 if (( $n_notable_differences ))
374386 then rule_w=$(( $local_w + $behind_w + $ahead_w + $remote_w + $INNER_PADDING_W ))
375-
376387 PrintHRule $rule_w
377388 for (( result_n = 0 ; result_n < $n_notable_differences ; result_n++ ))
378389 do PrintReportLine
379390 done
380391 PrintHRule $rule_w
381392 fi
382393
383- # print synchronized message if all compared upstreams had no diffs
394+ # print " synchronized" message if all compared upstreams had no divergence
384395 if (( $all_in_sync ))
385- then rule_w=$(( ${# no_results_msg} + 4 ))
396+ then l_border=" $DELIM " r_border=" $DELIM "
397+ borders_pad_w=$(( ${# l_border} + ${# r_border} ))
398+ wrap_w=$(( $(CurrentTtyW) - $MARGINS_PAD_W - $borders_pad_w ))
399+ synchronized_msg=$( echo " $synchronized_msg " | fold -s -w $wrap_w | tr ' ' " $JOIN_CHAR " )
400+ rule_w=0
401+ for line in $synchronized_msg
402+ do line=${line/% $JOIN_CHAR / }
403+ [ ${# line} -gt $rule_w ] && rule_w=${# line}
404+ done
405+ rule_w=$(( $rule_w + $MARGINS_PAD_W ))
386406
387407 PrintHRule $rule_w
388- echo -e " $DELIM $CEVEN$no_results_msg$CEND $DELIM "
408+ for line in $synchronized_msg
409+ do line=${line/% $JOIN_CHAR / }
410+ pad_w=$(( $rule_w - ${# line} - $MARGINS_PAD_W ))
411+ line_fmt=" %$(( $MARGIN_PAD_W )) s$l_border$CEVEN$line$CEND %$(( $pad_w )) s$r_border "
412+ printf " $line_fmt \n" | sed " $JOIN_REGEX "
413+ done
389414 PrintHRule $rule_w
390415 fi
391416
@@ -399,8 +424,8 @@ function PrintReportLine
399424 behind_msg=$( echo ${behind_msgs[$result_n]} | sed " $JOIN_REGEX " )
400425 ahead_msg=$( echo ${ahead_msgs[$result_n]} | sed " $JOIN_REGEX " )
401426 remote_msg=$( echo ${remote_msgs[$result_n]} | sed " $JOIN_REGEX " )
402- local_msg=" ${local_msg: 0: $local_w } " ;
403- remote_msg=" ${remote_msg: 0: $remote_w } " ;
427+ local_msg=" ${local_msg: 0: $local_w } "
428+ remote_msg=" ${remote_msg: 0: $remote_w } "
404429 local_color=" ${local_colors[$result_n]} "
405430 behind_color=" ${behind_colors[$result_n]} "
406431 ahead_color=" ${ahead_colors[$result_n]} "
@@ -415,16 +440,16 @@ function PrintReportLine
415440
416441 # build output messages and display
417442 if (( $(IsCurrentBranch $local_msg )) ) ; then star=$STAR ; else star=" " ; fi ;
418- local_msg=" %$(( $local_offset )) s$star $( echo -e $DELIM $local_color$local_msg$CEND ) "
443+ local_msg=" %$(( $local_offset )) s$star $( echo -e $DELIM $local_color$local_msg$CEND ) "
419444 behind_msg=" %$(( $behind_offset )) s $( echo -e $DELIM $behind_color$behind_msg$CEND ) "
420- ahead_msg=" %$(( $ahead_offset )) s $( echo -e $DELIM $ahead_color$ahead_msg$CEND ) "
445+ ahead_msg=" %$(( $ahead_offset )) s $( echo -e $DELIM $ahead_color$ahead_msg$CEND ) "
421446 remote_msg=" %$(( $remote_offset )) s $( echo -e $DELIM $remote_color$remote_msg$CEND ) "
422447 end_msg=" %$(( $end_offset )) s $DELIM "
423448 printf " $local_msg$behind_msg$ahead_msg$remote_msg$end_msg \n"
424449}
425450
426451
427- # ## main entry # ##
452+ # # main entry ##
428453
429454# parse CLI switches
430455show_dates=0
@@ -437,16 +462,17 @@ case "$1" in
437462 ' -a' |' --all' ) show_all=1 ;;
438463 ' -b' |' --branch' ) [ " $2 " ] && branch_a=" $2 " || branch_a=$( GetCurrentBranch) ;;
439464 ' -d' |' --dates' ) show_dates=1 ;;
440- ' -h' |' --help' ) echo " $USAGE " ; exit ;;
441- ' -l' |' --local' ) show_all_local=1 ; show_all_upstream=1 ; ;;
465+ ' -h' |' --help' ) EXIT " $USAGE " ;;
466+ ' -l' |' --local' ) show_all_local=1 show_all_upstream=1 ;;
442467 ' -r' |' --remotes' ) show_all_remote=1 ;;
443- ' -v' |' --verbose' ) show_all=1 ; show_dates=1 ;;
468+ ' -v' |' --verbose' ) show_all=1 show_dates=1 ;;
444469 * ) branch_a=" $1 " branch_b=" $2 " ;;
445470esac
446471
447- [ " $branch_a " ] && ! (( $(DoesBranchExist $branch_a )) ) && echo " no such branch: '$branch_a '" && exit
448- [ " $branch_b " ] && ! (( $(DoesBranchExist $branch_b )) ) && echo " no such branch: '$branch_b '" && exit
449- [ " $branch_a " ] && [ -z " $branch_b " ] && ! (( $(IsLocalBranch $branch_a )) ) && echo " no such local branch: '$branch_a '" && exit
472+ (( $(CurrentTtyW) < $MIN_TTY_W )) && EXIT " tty must be wider than $MIN_TTY_W chars"
473+ [ " $branch_a " ] && ! (( $(DoesBranchExist $branch_a )) ) && EXIT " no such branch: '$branch_a '"
474+ [ " $branch_b " ] && ! (( $(DoesBranchExist $branch_b )) ) && EXIT " no such branch: '$branch_b '"
475+ [ " $branch_a " ] && [ -z " $branch_b " ] && ! (( $(IsLocalBranch $branch_a )) ) && EXIT " no such local branch: '$branch_a '"
450476[ " $branch_a " ] && show_all_local=1 # force "no upstream" message for non-tracking branches
451477
452478readonly FILTER_BRANCH=$branch_a
@@ -459,17 +485,17 @@ readonly SHOW_ALL_REMOTE=$(($show_all + $show_all_remote)) # show all remote
459485
460486
461487if [ -z " $COMPARE_BRANCH " ]
462- then [ " $FILTER_BRANCH " ] && synched_msg =" $BRANCH_SYNCED_MSG " || synched_msg =" $LOCALS_SYNCED_MSG "
488+ then [ " $FILTER_BRANCH " ] && synchronized_msg =" $BRANCH_SYNCED_MSG " || synchronized_msg =" $LOCALS_SYNCED_MSG "
463489
464490 # compare sync status of local branches to their upstreams
465491 while read local upstream ; do GenerateReport $local $upstream ; done < <( GetLocalRefs) ;
466- PrintReport " local <-> upstream" " $synched_msg "
492+ PrintReport " local <-> upstream" " $synchronized_msg "
467493else is_tracked_branch=$( IsTrackedBranch $FILTER_BRANCH $COMPARE_BRANCH )
468- (( $is_tracked_branch )) && synched_msg =" $BRANCH_SYNCED_MSG " || synched_msg =" $UNTRACKED_SYNCHED_MSG "
494+ (( $is_tracked_branch )) && synchronized_msg =" $BRANCH_SYNCED_MSG " || synchronized_msg =" $UNTRACKED_SYNCHED_MSG "
469495
470496 # compare sync status of arbitrary branches per cli args
471497 GenerateReport $FILTER_BRANCH $COMPARE_BRANCH
472- PrintReport " $FILTER_BRANCH <-> $COMPARE_BRANCH " " $synched_msg "
498+ PrintReport " $FILTER_BRANCH <-> $COMPARE_BRANCH " " $synchronized_msg "
473499fi
474500
475501
0 commit comments