diff --git a/git-branch-status-subtree/.gitignore b/git-branch-status-subtree/.gitignore new file mode 100644 index 0000000..a06670d --- /dev/null +++ b/git-branch-status-subtree/.gitignore @@ -0,0 +1,5 @@ +* +!gbs-config.sh.inc.example +!git-branch-status +!LICENSE +!README.md diff --git a/git-branch-status-subtree/README.md b/git-branch-status-subtree/README.md index ecf3cb3..a0034dc 100644 --- a/git-branch-status-subtree/README.md +++ b/git-branch-status-subtree/README.md @@ -58,16 +58,16 @@ EXAMPLES: | feature-branch | (even) | (ahead 2) | origin/feature-branch | | master | (behind 1) | (even) | origin/master | - # compare two arbitrary branches (either local and either remote) + # compare two arbitrary branches - local or remote $ git-branch-status local-arbitrary-branch fork/arbitrary-branch | local-arbitrary-branch | (even) | (ahead 1) | fork/arbitrary-branch | $ git-branch-status fork/arbitrary-branch local-arbitrary-branch | fork/arbitrary-branch | (behind 1) | (even) | local-arbitrary-branch | - # show all branches - including those synchronized, non-tracking, or not checked-out + # show all branches - local and remote, regardless of state or relationship $ git-branch-status -a $ git-branch-status --all - | master | (even) | (ahead 1) | origin/master | + *| master | (even) | (ahead 1) | origin/master | | tracked-branch | (even) | (even) | origin/tracked-branch | | (no local) | n/a | n/a | origin/untracked-branch | | local-branch | n/a | n/a | (no upstream) | @@ -77,7 +77,7 @@ EXAMPLES: # show the current branch $ git-branch-status -b $ git-branch-status --branch - | current-branch | (even) | (ahead 2) | origin/current-branch | + *| current-branch | (even) | (ahead 2) | origin/current-branch | # show a specific branch $ git-branch-status specific-branch @@ -98,7 +98,7 @@ EXAMPLES: # show all local branches - including those synchronized or non-tracking $ git-branch-status -l $ git-branch-status --local - | master | (even) | (ahead 1) | origin/master | + *| master | (even) | (ahead 1) | origin/master | | tracked-branch | (even) | (even) | origin/tracked-branch | | local-branch | n/a | n/a | (no upstream) | @@ -111,9 +111,9 @@ EXAMPLES: # show all branches with timestamps (like -a -d) $ git-branch-status -v $ git-branch-status --verbose - | 1999-12-31 local | n/a | n/a | (no upstream) | - | 1999-12-31 master | (behind 1) | (even) | 2000-01-01 origin/master | - | 1999-12-31 tracked | (even) | (even) | 2000-01-01 origin/tracked | + | 1999-12-31 master | (behind 1) | (even) | 2000-01-01 origin/master | + | 1999-12-31 tracked | (even) | (even) | 2000-01-01 origin/tracked | + *| 1999-12-31 local-wip | n/a | n/a | (no upstream) | ``` diff --git a/git-branch-status-subtree/gbs-config.sh.inc.example b/git-branch-status-subtree/gbs-config.sh.inc.example new file mode 100644 index 0000000..f6f12e0 --- /dev/null +++ b/git-branch-status-subtree/gbs-config.sh.inc.example @@ -0,0 +1,28 @@ +############################################################################### +# optional user-defined config for the 'git-branch-status' program # +#-----------------------------------------------------------------------------# +# * copy this file to: ./gbs-config.sh.inc (or $GBS_CFG_FILE) to customize # +# * the text in this file must be valid BASH # +# * you can also export these configuration options from the environment # +# as well as an alternate location of this file (as $GBS_CFG_FILE) # +# * environment variables will over-ride those defined in this file # +# * environment variables should be named similarly to those in this file # +# with the CFG_* prefix replaced with GBS_* # +# eg: export GBS_CFG_FILE=~/.config/git-branch-status/config.sh.inc # +# eg: export GBS_FETCH_PERIOD=0 # +############################################################################### + + +# number of minutes between automatic `git fetch --all` calls +# 0 => always, -1 => never (default: -1) +readonly CFG_FETCH_PERIOD=-1 + + +# writable filesystem location for storage of the last-fetch timestamp +# (default: ~/.GBS_LAST_FETCH) +readonly CFG_LAST_FETCH_FILE=~/.GBS_LAST_FETCH + + +# whether or not to present ANSI colors +# 0 => no, 1 => yes (default: 1) +readonly CFG_USE_ANSI_COLOR=1 diff --git a/git-branch-status-subtree/git-branch-status b/git-branch-status-subtree/git-branch-status index a4a37af..8c237fd 100755 --- a/git-branch-status-subtree/git-branch-status +++ b/git-branch-status-subtree/git-branch-status @@ -5,7 +5,7 @@ # Copyright 2011 Jehiah Czebotar # Copyright 2013 Fredrik Strandin # Copyright 2014 Kristijan Novoselić -# Copyright 2014-2018 bill-auger +# Copyright 2014-2020 bill-auger # # git-branch-status is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License version 3 @@ -53,16 +53,16 @@ EXAMPLES: | feature-branch | (even) | (ahead 2) | origin/feature-branch | | master | (behind 1) | (even) | origin/master | - # compare two arbitrary branches (either local and either remote) + # compare two arbitrary branches - local or remote $ git-branch-status local-arbitrary-branch fork/arbitrary-branch | local-arbitrary-branch | (even) | (ahead 1) | fork/arbitrary-branch | $ git-branch-status fork/arbitrary-branch local-arbitrary-branch | fork/arbitrary-branch | (behind 1) | (even) | local-arbitrary-branch | - # show all branches - including those synchronized, non-tracking, or not checked-out + # show all branches - local and remote, regardless of state or relationship $ git-branch-status -a $ git-branch-status --all - | master | (even) | (ahead 1) | origin/master | + *| master | (even) | (ahead 1) | origin/master | | tracked-branch | (even) | (even) | origin/tracked-branch | | (no local) | n/a | n/a | origin/untracked-branch | | local-branch | n/a | n/a | (no upstream) | @@ -72,7 +72,7 @@ EXAMPLES: # show the current branch $ git-branch-status -b $ git-branch-status --branch - | current-branch | (even) | (ahead 2) | origin/current-branch | + *| current-branch | (even) | (ahead 2) | origin/current-branch | # show a specific branch $ git-branch-status specific-branch @@ -93,7 +93,7 @@ EXAMPLES: # show all local branches - including those synchronized or non-tracking $ git-branch-status -l $ git-branch-status --local - | master | (even) | (ahead 1) | origin/master | + *| master | (even) | (ahead 1) | origin/master | | tracked-branch | (even) | (even) | origin/tracked-branch | | local-branch | n/a | n/a | (no upstream) | @@ -106,12 +106,30 @@ EXAMPLES: # show all branches with timestamps (like -a -d) $ git-branch-status -v $ git-branch-status --verbose - | 1999-12-31 local | n/a | n/a | (no upstream) | - | 1999-12-31 master | (behind 1) | (even) | 2000-01-01 origin/master | - | 1999-12-31 tracked | (even) | (even) | 2000-01-01 origin/tracked | + | 1999-12-31 master | (behind 1) | (even) | 2000-01-01 origin/master | + | 1999-12-31 tracked | (even) | (even) | 2000-01-01 origin/tracked | + *| 1999-12-31 local-wip | n/a | n/a | (no upstream) | USAGE_MSG +## user-defined configuration (see ./gbs-config.sh.inc.example) ## + +readonly THIS_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}" 2>/dev/null || echo $0)")" +readonly DEF_CFG_FILE="${THIS_DIR}"/gbs-config.sh.inc +readonly CFG_FILE=$( ( [[ "$GBS_CFG_FILE" && -f "$GBS_CFG_FILE" ]] && echo -n "$GBS_CFG_FILE" ) || \ + ( [[ "$DEF_CFG_FILE" && -f "$DEF_CFG_FILE" ]] && echo -n "$DEF_CFG_FILE" ) ) +source "$CFG_FILE" 2> /dev/null +readonly FETCH_PERIOD=$( ( [[ "$GBS_FETCH_PERIOD" =~ ^-?[0-9]+$ ]] && echo -n "$GBS_FETCH_PERIOD" ) || \ + ( [[ "$CFG_FETCH_PERIOD" =~ ^-?[0-9]+$ ]] && echo -n "$CFG_FETCH_PERIOD" ) || \ + echo -n '-1' ) +readonly LAST_FETCH_FILE=$( ( touch "$GBS_LAST_FETCH_FILE" 2> /dev/null && echo -n "$GBS_LAST_FETCH_FILE" ) || \ + ( touch "$CFG_LAST_FETCH_FILE" 2> /dev/null && echo -n "$CFG_LAST_FETCH_FILE" ) || \ + echo -n ~/.GBS_LAST_FETCH ) +readonly USE_ANSI_COLOR=$( ( [[ "$GBS_USE_ANSI_COLOR" =~ ^[01]$ ]] && echo -n "$GBS_USE_ANSI_COLOR" ) || \ + ( [[ "$CFG_USE_ANSI_COLOR" =~ ^[01]$ ]] && echo -n "$CFG_USE_ANSI_COLOR" ) || \ + echo -n '1' ) + + ## constants ## readonly INNER_PADDING_W=13 # '| ' + ' | ' + ' | ' + ' | ' + ' |' @@ -119,14 +137,14 @@ readonly MARGIN_PAD_W=2 readonly MARGINS_PAD_W=$(( $MARGIN_PAD_W * 2 )) readonly ALL_PADDING_W=$(( $INNER_PADDING_W + $MARGINS_PAD_W )) readonly MAX_DIVERGENCE_W=12 # e.g. "(behind 999)" -readonly MIN_TTY_W=60 # ASSERT: (0 < ($ALL_PADDING_W + (MAX_DIVERGENCE_W * 2)) <= $MIN_TTY_W) +readonly MIN_TTY_W=60 # ASSERT: (0 < (ALL_PADDING_W + (MAX_DIVERGENCE_W * 2)) <= MIN_TTY_W) readonly MARGIN_PAD=$(printf "%$(( $MARGIN_PAD_W ))s") -readonly CWHITE='\033[0;37m' -readonly CRED='\033[0;31m' -readonly CGREEN='\033[0;32m' -readonly CYELLOW='\033[1;33m' -readonly CBLUE='\033[1;34m' -readonly CEND='\033[0m' +readonly CWHITE=$( (( $USE_ANSI_COLOR )) && echo '\033[0;37m' ) +readonly CRED=$( (( $USE_ANSI_COLOR )) && echo '\033[0;31m' ) +readonly CGREEN=$( (( $USE_ANSI_COLOR )) && echo '\033[0;32m' ) +readonly CYELLOW=$( (( $USE_ANSI_COLOR )) && echo '\033[1;33m' ) +readonly CBLUE=$( (( $USE_ANSI_COLOR )) && echo '\033[1;34m' ) +readonly CEND=$( (( $USE_ANSI_COLOR )) && echo '\033[0m' ) readonly CDEFAULT=$CWHITE readonly CTRACKING=$CBLUE readonly CAHEAD=$CYELLOW @@ -184,42 +202,44 @@ declare -a RemoteColors=() ## helpers ## -function AssertIsValidRepo +AssertIsValidRepo() { [ "$(git rev-parse --is-inside-work-tree 2> /dev/null)" == 'true' ] || \ ! (( $(AssertIsNotBareRepo) )) && echo 1 || echo 0 } -function AssertHasCommits +AssertHasCommits() { [ "$(git cat-file -t HEAD 2> /dev/null)" ] && echo 1 || echo 0 } -function AssertIsNotBareRepo +AssertIsNotBareRepo() { [ "$(git rev-parse --is-bare-repository 2> /dev/null)" != 'true' ] && echo 1 || echo 0 } -function GetRefs # (refs_dir) +GetRefs() # (refs_dir) { local refs_dir=$1 + local fmt='%(refname:short) %(upstream:short)' + local sort=$( (( $SHOW_DATES )) && echo '--sort=creatordate') - git for-each-ref --format="%(refname:short) %(upstream:short)" $refs_dir 2> /dev/null + git for-each-ref --format="$fmt" $sort $refs_dir 2> /dev/null } -function GetLocalRefs +GetLocalRefs() { GetRefs refs/heads } -function GetRemoteRefs # (remote_repo_name) +GetRemoteRefs() # (remote_repo_name) { local remote_repo=$1 GetRefs refs/remotes/$remote_repo } -function GetStatus # (base_commit compare_commit) +GetStatus() # (base_commit compare_commit) { local base_commit=$1 local compare_commit=$2 @@ -227,19 +247,19 @@ function GetStatus # (base_commit compare_commit) git rev-list --left-right ${base_commit}...${compare_commit} -- 2>/dev/null } -function GetCurrentBranch +GetCurrentBranch() { git rev-parse --abbrev-ref HEAD } -function GetUpstreamBranch # (local_branch) +GetUpstreamBranch() # (local_branch) { local local_branch=$1 git rev-parse --abbrev-ref $local_branch@{upstream} 2> /dev/null } -function IsCurrentBranch # (branch_name) +IsCurrentBranch() # (branch_name) { local branch=$1 local this_branch=$(AppendHeadDate $branch) @@ -248,7 +268,7 @@ function IsCurrentBranch # (branch_name) [ "$this_branch" == "$current_branch" ] && echo 1 || echo 0 } -function IsLocalBranch # (branch_name) +IsLocalBranch() # (branch_name) { local branch=$1 local is_local_branch=$(git branch -a | grep -E "^.* $branch$") @@ -256,7 +276,7 @@ function IsLocalBranch # (branch_name) [ "$is_local_branch" ] && echo 1 || echo 0 } -function IsTrackedBranch # (base_branch_name compare_branch_name) +IsTrackedBranch() # (base_branch_name compare_branch_name) { local base_branch=$1 local compare_branch=$2 @@ -265,7 +285,7 @@ function IsTrackedBranch # (base_branch_name compare_branch_name) [ "$compare_branch" == "$upstream_branch" ] && echo 1 || echo 0 } -function DoesBranchExist # (branch_name) +DoesBranchExist() # (branch_name) { local branch=$1 local is_known_branch=$(git branch -a | grep -E "^.* (remotes\/)?$branch$") @@ -273,7 +293,7 @@ function DoesBranchExist # (branch_name) [ "$is_known_branch" ] && echo 1 || echo 0 } -function AppendHeadDate # (commit_ref) +AppendHeadDate() # (commit_ref) { local commit=$1 local author_date=$(git log -n 1 --format=format:"%ai" $commit 2> /dev/null) @@ -284,7 +304,7 @@ function AppendHeadDate # (commit_ref) echo $date$commit } -function CurrentTtyW +CurrentTtyW() { local tty_dims=$(stty -F /dev/tty size 2> /dev/null || stty -f /dev/tty size 2> /dev/null) local tty_w=$(echo $tty_dims | cut -d ' ' -f 2) @@ -292,7 +312,7 @@ function CurrentTtyW (( $tty_w )) && echo "$tty_w" || echo "$MIN_TTY_W" } -function PrintHRule # (rule_width) +PrintHRule() # (rule_width) { local rule_w=$1 local h_rule="$(dd if=/dev/zero bs=$rule_w count=1 2> /dev/null | tr '\0' $HRULE_CHAR)" @@ -300,7 +320,7 @@ function PrintHRule # (rule_width) echo "$MARGIN_PAD$h_rule" } -function EXIT # (exit_msg exit_status) +Exit() # (exit_msg exit_status) { local exit_msg=$1 local exit_status=$2 @@ -314,7 +334,7 @@ function EXIT # (exit_msg exit_status) ## business ## -function Init # (cli_args) +Init() # (cli_args) { local show_dates=0 local show_all=0 @@ -328,8 +348,8 @@ function Init # (cli_args) case "$1" in '-a'|'--all' ) show_all=1 ;; '-b'|'--branch' ) [ "$2" ] && branch_a="$2" || branch_a=$(GetCurrentBranch) ;; - '-d'|'--dates' ) show_dates=1 ;; - '-h'|'--help' ) EXIT "$USAGE" $EXIT_SUCCESS ;; + '-d'|'--dates' ) branch_a="$2" ; branch_b="$3" ; show_dates=1 ;; + '-h'|'--help' ) Exit "$USAGE" $EXIT_SUCCESS ;; '-l'|'--local' ) show_all_local=1 ; show_all_synced=1 ; ;; '-r'|'--remotes' ) show_all_remote=1 ; show_all_synced=1 ; ;; '-v'|'--verbose' ) show_all=1 ; show_dates=1 ; ;; @@ -337,13 +357,13 @@ function Init # (cli_args) esac # sanity checks - (( $(AssertIsValidRepo ) )) || EXIT "$NOT_REPO_ERR" - (( $(AssertIsNotBareRepo) )) || EXIT "$BARE_REPO_MSG" - (( $(AssertHasCommits ) )) || EXIT "$NO_COMMITS_MSG" - (( $(CurrentTtyW) >= $MIN_TTY_W )) || EXIT "$TTY_W_MSG" - [ -z "$branch_a" ] || (($(DoesBranchExist $branch_a))) || EXIT "$INVALID_BRANCH_MSG '$branch_a'" - [ -z "$branch_b" ] || (($(DoesBranchExist $branch_b))) || EXIT "$INVALID_BRANCH_MSG '$branch_b'" - [ -z "$branch_a" ] || (($(IsLocalBranch $branch_a))) || [ "$branch_b" ] || EXIT "$INVALID_LOCAL_BRANCH_MSG '$branch_a'" + (( $(AssertIsValidRepo ) )) || Exit "$NOT_REPO_ERR" + (( $(AssertIsNotBareRepo) )) || Exit "$BARE_REPO_MSG" + (( $(AssertHasCommits ) )) || Exit "$NO_COMMITS_MSG" + (( $(CurrentTtyW) >= $MIN_TTY_W )) || Exit "$TTY_W_MSG" + [ -z "$branch_a" ] || (($(DoesBranchExist $branch_a))) || Exit "$INVALID_BRANCH_MSG '$branch_a'" + [ -z "$branch_b" ] || (($(DoesBranchExist $branch_b))) || Exit "$INVALID_BRANCH_MSG '$branch_b'" + [ -z "$branch_a" ] || (($(IsLocalBranch $branch_a))) || [ "$branch_b" ] || Exit "$INVALID_LOCAL_BRANCH_MSG '$branch_a'" [ -z "$branch_a" ] || show_all_local=1 # force "no upstream" message for non-tracking branches readonly SHOW_DATES=$show_dates @@ -354,7 +374,20 @@ function Init # (cli_args) readonly COMPARE_BRANCH=$branch_b } -function GenerateReports +FetchRemotes() +{ + if (( ${FETCH_PERIOD} >= 0 )) + then local now_ts=$(date +%s) + local last_fetch_ts=$(( $(cat ${LAST_FETCH_FILE} 2> /dev/null) + 0 )) + + if (( ${now_ts} - ${last_fetch_ts} >= ${FETCH_PERIOD} * 60 )) + then git fetch --all + echo -n ${now_ts} > ${LAST_FETCH_FILE} + fi + fi +} + +GenerateReports() { if [ "$COMPARE_BRANCH" ] then CustomReport $(IsTrackedBranch $FILTER_BRANCH $COMPARE_BRANCH) @@ -363,7 +396,7 @@ function GenerateReports fi } -function CustomReport # (is_tracked_branch) +CustomReport() # (is_tracked_branch) { local is_tracked_branch=$1 local synchronized_msg @@ -374,7 +407,7 @@ function CustomReport # (is_tracked_branch) PrintReport "$FILTER_BRANCH <-> $COMPARE_BRANCH" "$synchronized_msg" } -function LocalReport +LocalReport() { local synchronized_msg [ "$FILTER_BRANCH" ] && synchronized_msg="$BRANCH_SYNCED_MSG" || synchronized_msg="$LOCALS_SYNCED_MSG" @@ -384,7 +417,7 @@ function LocalReport PrintReport "local <-> upstream" "$synchronized_msg" } -function RemoteReport +RemoteReport() { local synchronized_msg="$REMOTES_SYNCED_MSG" local remote_repo @@ -401,7 +434,7 @@ function RemoteReport done } -function GenerateReport # (base_branch compare_branch) +GenerateReport() # (base_branch compare_branch) { local base_branch=$1 local compare_branch=$2 @@ -479,7 +512,7 @@ function GenerateReport # (base_branch compare_branch) if [ ${#remote_msg} -gt $RemoteW ] ; then RemoteW=${#remote_msg} ; fi ; } -function PrintReport # (table_header_line synchronized_msg) +PrintReport() # (table_header_line synchronized_msg) { local table_header_line=$1 local synchronized_msg=$2 @@ -546,7 +579,7 @@ function PrintReport # (table_header_line synchronized_msg) Reset } -function PrintReportLine +PrintReportLine() { # select data local local_msg=$( echo ${LocalMsgs[ $result_n]} | sed "$JOIN_REGEX") @@ -578,7 +611,7 @@ function PrintReportLine printf "$local_msg$behind_msg$ahead_msg$remote_msg$end_msg\n" } -function Reset +Reset() { WereAnyDivergences=0 WereAnyCompared=0 @@ -600,4 +633,5 @@ function Reset ## main entry ## Init $* +FetchRemotes GenerateReports