Skip to content

Commit d1d9654

Browse files
committed
implement comparison of arbitrary branches
1 parent 99de59c commit d1d9654

File tree

2 files changed

+142
-83
lines changed

2 files changed

+142
-83
lines changed

README.md

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ A number of command-line switches exist, selecting various reports that compare
1212

1313
* This is showing the exhaustive '--all' report. Other reports are constrained (see 'USAGE' below).
1414
* The "local <-> upstream" section is itemizing all local branches. In this instance:
15-
* The local branch 'deleteme' has no remote tracking branch.
15+
* The local branch 'deleteme' is not tracking any remote branch.
1616
* The local branch 'kd35a' is tracking remote branch 'kd35a/master'.
1717
* The local branch 'knovoselic' is tracking remote branch 'knovoselic/master'.
1818
* The local branch 'master' is tracking remote branch 'origin/master'.
@@ -23,24 +23,27 @@ A number of command-line switches exist, selecting various reports that compare
2323
* The "local <-> origin" section is itemizing all branches on the 'origin' remote. In this instance:
2424
* The remote branch 'origin/delete-me' is not checked-out locally.
2525
* The remote branch 'origin/master' is tracked by the local branch 'master'.
26-
* The asterisks to the left of local 'master' branch names indicate the current working branch.
26+
* The asterisks to the left of the local 'master' branch names indicate the current working branch.
2727
* The blue branch names indicate a tracking relationship between a local and it's upstream branch.
28-
* The "local <-> upstream" section relates tracking branches while remote-specific sections relate identically named branches. This distinction determines the semantics of the green "Everything is synchronized" message.
29-
* In the "local <-> upstream" section, this indicates that all local branches which are tracking an upstream are up-to-date with their respective upstream counterparts.
30-
* In remote-specific sections, this indicates that all local branches which have the same name as some branch on this remote are up-to-date with that remote branch.
31-
* In single branch reports, this indicates that the local branch is up-to-date with it's upstream counterpart if it is tracking an upstream; otherwise this will always be shown.
28+
* The "local <-> upstream" section relates tracking branches while remote-specific sections relate identically named branches. This distinction determines the semantics of the green "... synchronized" messages.
29+
* In the "local <-> upstream" section, this indicates that all local branches which are tracking an upstream are synchronized with their respective upstream counterparts.
30+
* In remote-specific sections, this indicates that all local branches which have the same name as some branch on this remote are synchronized with that remote branch.
31+
* In single branch reports, this indicates that the local branch is tracking an upstream branch and is synchronized with it's upstream counterpart.
32+
* In arbitrary branch comparison reports, this indicates that the two compared branches are synchronized with each other.
3233

3334

3435
```
3536
USAGE:
3637
3738
git-branch-status
38-
git-branch-status [-a | --all]
39-
git-branch-status [-b | --branch] [branch-name]
40-
git-branch-status [-d | --dates]
41-
git-branch-status [-h | --help]
42-
git-branch-status [-r | --remotes]
43-
git-branch-status [-v | --verbose]
39+
git-branch-status [ base-branch-name compare-branch-name ]
40+
git-branch-status [ -a | --all ]
41+
git-branch-status [ -b | --branch ] [filter-branch-name]
42+
git-branch-status [ -d | --dates ]
43+
git-branch-status [ -h | --help ]
44+
git-branch-status [ -r | --remotes ]
45+
git-branch-status [ -v | --verbose ]
46+
4447
4548
EXAMPLES:
4649
@@ -50,7 +53,13 @@ EXAMPLES:
5053
| feature-branch | (even) | (ahead 2) | origin/feature-branch |
5154
| master | (behind 1) | (even) | origin/master |
5255
53-
# show all branches - including those up-to-date, with no upstream, or not checked-out
56+
# compare two arbitrary branches (either local and either remote)
57+
$ git-branch-status local-arbitrary-branch fork/arbitrary-branch
58+
| local-arbitrary-branch | (even) | (ahead 1) | fork/arbitrary-branch |
59+
$ git-branch-status fork/arbitrary-branch local-arbitrary-branch
60+
| fork/arbitrary-branch | (behind 1) | (even) | local-arbitrary-branch |
61+
62+
# show all branches - including those synchronized, with no upstream, or not checked-out
5463
$ git-branch-status -a
5564
$ git-branch-status --all
5665
| master | (even) | (ahead 1) | origin/master |

git-branch-status

Lines changed: 120 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
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>
@@ -20,10 +20,11 @@
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'
3435
USAGE:
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
4447
EXAMPLES:
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='*'
118127
readonly DELIM='|'
119128
readonly NO_UPSTREAM="(no${JOIN_CHAR}upstream)"
120129
readonly 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
126139
local_w=0
127140
behind_w=0
128141
ahead_w=0
@@ -181,19 +194,34 @@ function GetUpstreamBranch # (local_branch)
181194
function 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

190219
function 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

199227
function AppendHeadDate # (commit_ref)
@@ -221,7 +249,8 @@ function PrintHRule # (rule_width)
221249

222250
function 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

239268
function 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
374411
show_all_remote=0
375412

376413
case "$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" ;;
384421
esac
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
389430
readonly SHOW_DATES=$show_dates
390431
readonly SHOW_ALL=$show_all
391432
readonly SHOW_ALL_LOCAL=$(($show_all + $show_all_local)) # show local branches that are not tracking any upstream
392433
readonly SHOW_ALL_UPSTREAM=$(($show_all + $show_all_upstream)) # show local branches that are synchronized with their upstream
393434
readonly 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
404455
for 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"
413463
done

0 commit comments

Comments
 (0)