Skip to content

Commit 5f8abfc

Browse files
committed
format into colums - add filters - add usage message (DEBUG_VERSION)
* prettified into columns * added ability to filter by branch name and to show locals * removed the temp file i/o - runs fully in memory * added some switches and usage message * debug traces remain but commented
1 parent 87302cb commit 5f8abfc

File tree

1 file changed

+223
-28
lines changed

1 file changed

+223
-28
lines changed

git-branch-status

Lines changed: 223 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,233 @@
11
#!/bin/bash
2-
# by http://github.com/jehiah
3-
# this prints out some branch status (similar to the '... ahead' info you get from git status)
2+
# git-branch-status DEBUG
3+
# * originally by http://github.com/jehiah
4+
# * "s'all good!" message by http://github.com/kd35a
5+
# * ANSI colors by http://github.com/knovoselic
6+
# * column formatting and filters by http://github.com/bill-auger
47

5-
# example:
6-
# $ git branch-status
7-
# dns_check (ahead 1) | (behind 112) origin/master
8-
# master (ahead 2) | (behind 0) origin/master
8+
# this script prints out pretty git branch sync status reports
99

1010

11-
TOTAL_DIFFERENCES=0
12-
CGREEN='\033[1;32m'
13-
CYELLOW='\033[1;33m'
14-
CRED='\033[1;31m'
15-
CEND='\033[0m'
11+
USAGE=<<USAGE
12+
usage: git-branch-status
13+
git-branch-status [-a | --all]
14+
git-branch-status [-h | --help]
15+
git-branch-status [-b | --branch [branch-name]] [branch-name]
1616
17+
$ git-branch-status
18+
| dns_check | (ahead 1) | (behind 112) | origin/dns_check |
19+
| master | (ahead 2) | (behind 0) | origin/master |
1720
21+
$ git-branch-status --all
22+
| a-local-branch | n/a | n/a | n/a |
23+
| a-remote-branch | (even) | (even) | origin/a-remote-branch |
24+
| dns_check | (ahead 1) | (behind 112) | origin/dns_check |
25+
| master | (ahead 2) | (behind 0) | origin/master |
26+
27+
$ git-branch-status -b
28+
$ git-branch-status --branch
29+
| current-branch | (ahead 2) | (behind 0) | origin/even-branch |
30+
31+
$ git-branch-status -h
32+
$ git-branch-status --help
33+
prints this message
34+
35+
$ git-branch-status specific-branch
36+
$ git-branch-status -b specific-branch
37+
$ git-branch-status --branch specific-branch
38+
| specific-branch | (ahead 2) | (behind 0) | origin/even-branch |
39+
USAGE
40+
41+
42+
# switches
43+
function current_branch() { echo $(git rev-parse --abbrev-ref HEAD) ; }
44+
45+
function set_filter_or_die
46+
{
47+
if [ "$(current_branch)" == "$1" ] || [ $(git branch | grep -G "^ $1$") ] ; then
48+
branch=$1
49+
else echo "no such branch: '$1'" ; exit ;
50+
fi
51+
}
52+
53+
if [ $1 ] ; then
54+
if [ "$1" == "-h" -o "$1" == "--help" ] ; then echo $USAGE ; exit ;
55+
elif [ "$1" == "-a" -o "$1" == "--all" ] ; then
56+
readonly SHOW_ALL=1
57+
elif [ "$1" == "-b" -o "$1" == "--branch" ] ; then
58+
if [ $2 ] ; then set_filter_or_die $2 ; else branch=$(current_branch) ; fi ;
59+
else set_filter_or_die $1
60+
fi
61+
fi
62+
63+
64+
# constants
65+
readonly SHOW_ALL_REMOTE=$(($SHOW_ALL + 0)) # also show branches that are up to date
66+
readonly SHOW_ALL_LOCAL=$(($SHOW_ALL + 0)) # also show branches that have no remote counterpart
67+
readonly CGREEN='\033[0;32m'
68+
readonly CYELLOW='\033[1;33m'
69+
readonly CRED='\033[0;31m'
70+
readonly CEND='\033[0m'
71+
readonly SPACER="|"
72+
readonly CAHEAD=$CYELLOW
73+
readonly CBEHIND=$CRED
74+
readonly CEVEN=$CGREEN
75+
readonly NO_RESULTS_MSG="${CEVEN}Everything is synchronized.$CEND"
76+
77+
78+
# variables
79+
n_total_differences=0
80+
local_col_w=0
81+
ahead_col_w=0
82+
behind_col_w=0
83+
remote_col_w=0
84+
declare -a local_msgs=()
85+
declare -a ahead_msgs=()
86+
declare -a behind_msgs=()
87+
declare -a remote_msgs=()
88+
declare -a ahead_colors=()
89+
declare -a behind_colors=()
90+
91+
92+
# if [ $SHOW_ALL_REMOTE -eq 1 ] ; then echo "show diffs" ; else echo "no diffs" ; fi ;
93+
# if [ $SHOW_ALL_LOCAL -eq 1 ] ; then echo "show locals" ; else echo "no locals" ; fi ;
94+
95+
96+
# loop over all branches
1897
while read local remote
1998
do
20-
[ -z "$remote" ] && continue
21-
git rev-list --left-right ${local}...${remote} -- 2>/dev/null >/tmp/git_upstream_status_delta || continue
22-
LEFT_AHEAD=$(grep -c '^<' /tmp/git_upstream_status_delta)
23-
RIGHT_AHEAD=$(grep -c '^>' /tmp/git_upstream_status_delta)
24-
TOTAL_DIFFERENCES=$(($LEFT_AHEAD + $RIGHT_AHEAD + $TOTAL_DIFFERENCES))
25-
MSG_LEFT_AHEAD="(ahead $LEFT_AHEAD)"
26-
MSG_RIGHT_AHEAD="(behind $RIGHT_AHEAD)"
27-
if [ "$LEFT_AHEAD" -ne 0 ]; then
28-
MSG_LEFT_AHEAD="$CYELLOW$MSG_LEFT_AHEAD$CEND"
29-
fi
30-
if [ "$RIGHT_AHEAD" -ne 0 ]; then
31-
MSG_RIGHT_AHEAD="$CRED$MSG_RIGHT_AHEAD$CEND"
32-
fi
33-
echo -e "$local $MSG_LEFT_AHEAD | $MSG_RIGHT_AHEAD $remote"
99+
# echo ; echo "$local <-> $remote IN"
100+
# if [ $branch ] && [ "$branch" != "$local" ] ; then echo "skipping branch '$local'" ; fi ;
101+
# if [ ! -z "$remote" ] ; then echo "has remote" ; else echo "no remote - bailing" ; fi ;
102+
103+
# filter branches by name
104+
[ $branch ] && [ "$branch" != "$local" ] && continue
105+
106+
# parse local<->remote sync status
107+
if [ ! -z "$remote" ] ; then
108+
status=$(git rev-list --left-right ${local}...${remote} -- 2>/dev/null)
109+
[ $(($?)) -eq 0 ] || continue
110+
n_ahead=$(echo $status | tr " " "\n" | grep -c '^<')
111+
n_behind=$(echo $status | tr " " "\n" | grep -c '^>')
112+
n_differences=$(($n_ahead + $n_behind))
113+
n_total_differences=$(($n_total_differences + $n_differences))
114+
115+
# echo "SHOW_ALL_REMOTE=$SHOW_ALL_REMOTE n_ahead=$n_ahead n_behind=$n_behind n_total_differences =$n_total_differences"
116+
117+
# filter branches by status
118+
[ "$SHOW_ALL_REMOTE" -eq 0 -a "$n_differences" -eq 0 ] && continue
119+
120+
# color output
121+
if [ "$n_ahead" -ne 0 ] ; then ahead_color=$CAHEAD ; else ahead_color=$CEVEN ; fi ;
122+
if [ "$n_behind" -ne 0 ] ; then behind_color=$CBEHIND ; else behind_color=$CEVEN ; fi ;
123+
elif [ "$SHOW_ALL_LOCAL" -eq 1 ] ; then
124+
n_ahead="X" ; n_behind="X" ; remote="n/a" ; ahead_color="$CEVEN" ; behind_color="$CEVEN"
125+
else continue
126+
fi
127+
128+
# populate lists
129+
local_msgs=( ${local_msgs[@]} "$local" )
130+
ahead_msgs=( ${ahead_msgs[@]} "$n_ahead" )
131+
behind_msgs=( ${behind_msgs[@]} "$n_behind" )
132+
remote_msgs=( ${remote_msgs[@]} "$remote" )
133+
ahead_colors=( ${ahead_colors[@]} "$ahead_color" )
134+
behind_colors=( ${behind_colors[@]} "$behind_color" )
135+
136+
# echo "$local <-> $remote OUT"
137+
# echo "col_ws IN=[ ${#local} ${#n_ahead} ${#n_behind} ${#remote} ]"
138+
# echo "msg=*$local*$n_ahead*$n_behind*$remote*"
139+
140+
# determine max column widths
141+
if [ ${#local} -gt $local_col_w ] ; then local_col_w=${#local} ; fi ;
142+
if [ ${#n_ahead} -gt $ahead_col_w ] ; then ahead_col_w=${#n_ahead} ; fi ;
143+
if [ ${#n_behind} -gt $behind_col_w ] ; then behind_col_w=${#n_behind} ; fi ;
144+
if [ ${#remote} -gt $remote_col_w ] ; then remote_col_w=${#remote} ; fi ;
145+
34146
done < <(git for-each-ref --format="%(refname:short) %(upstream:short)" refs/heads)
35147

36-
if [ "$TOTAL_DIFFERENCES" == 0 ]; then
37-
echo -e "${CGREEN}Everything is synchronized.$CEND"
38-
fi
148+
# echo "n_results=${#local_msgs[@]}"
149+
# compensate for "(ahead )" and "(behind )" to be appended
150+
ahead_col_w=$(($ahead_col_w + 8))
151+
behind_col_w=$(($behind_col_w + 10))
152+
153+
# pretty print results
154+
for (( result_n = 0 ; result_n < ${#local_msgs[@]} ; result_n++ ))
155+
do
156+
# echo
157+
158+
# pad "( 9 ahead)" like "( 99 ahead)" like "(999 ahead)"
159+
# ahead_pad_w=$(($ahead_col_w - ${#ahead_msgs[$result_n]} - 8))
160+
# behind_pad_w=$(($behind_col_w - ${#behind_msgs[$result_n]} - 9))
161+
# ahead_pad="'%"$ahead_pad_w"s'"
162+
# behind_pad="'%"$behind_pad_w"s'"
163+
# # echo "ahead_pad=$ahead_pad ahead_pad_w=$ahead_pad_w"
164+
# # echo "behind_pad_w=$behind_pad_w"
165+
166+
# ahead_msg=$(printf "(${ahead_msgs[$result_n]} "$ahead_pad"ahead)")
167+
# behind_msg=$(printf "(${behind_msgs[$result_n]} "$behind_pad"behind)")
168+
# echo "'$head_msg' head_msg_len=${#head_msg}"
169+
# echo "'${ahead_msg}' ahead_msg_len=${#ahead_msg}"
170+
171+
# behind_col_offset=$(($ahead_col_w - ${#ahead_msg} - 8 - $ahead_pad_w))
172+
# remote_col_offset=$(($behind_col_w - ${#behind_msg} - 9 - $behind_pad_w))
173+
# ahead_msg="("$ahead_pad" $ahead_msg ahead)"
174+
# behind_msg="("$behind_pad"$behind_msg behind)"
175+
176+
# fetch and filter data
177+
local_msg="${local_msgs[$result_n]}"
178+
ahead_msg="(ahead ${ahead_msgs[$result_n]})"
179+
behind_msg="(behind ${behind_msgs[$result_n]})"
180+
remote_msg="${remote_msgs[$result_n]}"
181+
ahead_color="${ahead_colors[$result_n]}"
182+
behind_color="${behind_colors[$result_n]}"
183+
if [ "$remote_msg" == "n/a" ] ; then ahead_msg="n/a" ; ahead_color="" ; fi ;
184+
if [ "$remote_msg" == "n/a" ] ; then behind_msg="n/a" ; behind_color="" ; fi ;
185+
if [ "$ahead_msg" == "(0 ahead)" ] ; then ahead_msg="(even)" ; fi ;
186+
if [ "$behind_msg" == "(0 behind)" ] ; then behind_msg="(even)" ; fi ;
187+
188+
# echo "local_msg='${local_msgs[$result_n]}'"
189+
# echo "ahead_msg='${ahead_msgs[$result_n]}'"
190+
# echo "behind_msg='${behind_msgs[$result_n]}'"
191+
# echo "remote_msgs='${remote_msgs[$result_n]}'"
192+
193+
# echo "local_msg IN='$local_msg' (${#local_msg})"
194+
# echo "ahead_msg IN='$ahead_msg' (${#ahead_msg})"
195+
# echo "behind_msg IN='$behind_msg' (${#behind_msg})"
196+
# echo "remote_msgs IN='$remote_msg' (${#remote_msg})"
197+
198+
# echo "ahead_color=$ahead_color"
199+
# echo "behind_color=$behind_color"
200+
201+
# calculate column offsets
202+
local_col_offset=1
203+
ahead_col_offset=$(($local_col_w - ${#local_msg}))
204+
behind_col_offset=$(($ahead_col_w - ${#ahead_msg}))
205+
remote_col_offset=$(($behind_col_w - ${#behind_msg}))
206+
end_col_offset=$(($remote_col_w - ${#remote_msg}))
207+
208+
# echo "local_col_offset =$local_col_w - ${#local_msg} = $ahead_col_offset"
209+
# echo "ahead_col_offset =$ahead_col_w - ${#ahead_msg} = $behind_col_offset"
210+
# echo "remote_col_offset=$behind_col_w - ${#behind_msg} = $remote_col_offset"
211+
# echo "end_col_offset =$remote_col_w - ${#remote_msg} = $end_col_offset"
212+
# echo "col_ws OUT=[ $local_col_w $ahead_col_w $behind_col_w $remote_col_w ]"
213+
# echo "this_w OUT=[ ${#local_msg} ${#ahead_msg} ${#behind_msg} ${#remote_msg} ]"
214+
# echo "offsets =[ $ahead_col_offset $behind_col_offset $remote_col_offset $end_col_offset ]"
215+
216+
# build output messages and print
217+
local_msg="%$(($local_col_offset))s $(echo -e $SPACER $local_msg)"
218+
ahead_msg="%$(($ahead_col_offset))s $(echo -e $SPACER $ahead_color$ahead_msg$CEND)"
219+
behind_msg="%$(($behind_col_offset))s $(echo -e $SPACER $behind_color$behind_msg$CEND)"
220+
remote_msg="%$(($remote_col_offset))s $(echo -e $SPACER $remote_msg)"
221+
end_msg="%$(($end_col_offset))s $SPACER"
222+
223+
# echo "local_msg OUT='$local_msg'"
224+
# echo "ahead_msg OUT='$ahead_msg'"
225+
# echo "behind_msg OUT='$behind_msg'"
226+
# echo "remote_msgs OUT='$remote_msg'"
227+
# echo "msg=*$local_msg}*$ahead_msg}*$behind_msg}*$remote_msg}*"
228+
229+
printf "$local_msg$ahead_msg$behind_msg$remote_msg$end_msg\n"
230+
done
231+
232+
# print something if no diffs
233+
if [ "$n_total_differences" == 0 ] ; then echo -e $NO_RESULTS_MSG ; fi ;

0 commit comments

Comments
 (0)