From 150e82b221e38751ca663aa85985aa603152eb19 Mon Sep 17 00:00:00 2001 From: Dan Wendorf Date: Sun, 6 Aug 2017 12:55:47 -0700 Subject: [PATCH] Refactor git functionality into githelpers.theme.bash - Add unit tests around git functionality --- bash_it.sh | 4 +- test/themes/base.theme.git.bats | 371 ++++++++++++++++++++++++++++++++ themes/base.theme.bash | 168 ++++----------- themes/githelpers.theme.bash | 157 ++++++++++++++ 4 files changed, 571 insertions(+), 129 deletions(-) create mode 100644 test/themes/base.theme.git.bats create mode 100644 themes/githelpers.theme.bash diff --git a/bash_it.sh b/bash_it.sh index 5371fc9c..70082205 100755 --- a/bash_it.sh +++ b/bash_it.sh @@ -55,9 +55,11 @@ do _load_bash_it_files $file_type done -# Load colors first so they can be used in base theme +# Load colors and helpers first so they can be used in base theme # shellcheck source=./themes/colors.theme.bash source "${BASH_IT}/themes/colors.theme.bash" +# shellcheck source=./themes/githelpers.theme.bash +source "${BASH_IT}/themes/githelpers.theme.bash" # shellcheck source=./themes/base.theme.bash source "${BASH_IT}/themes/base.theme.bash" diff --git a/test/themes/base.theme.git.bats b/test/themes/base.theme.git.bats new file mode 100644 index 00000000..2f3950b0 --- /dev/null +++ b/test/themes/base.theme.git.bats @@ -0,0 +1,371 @@ +#!/usr/bin/env bats + +load ../test_helper +load ../../lib/composure + +cite _about _param _example _group _author _version + +load ../../lib/helpers +load ../../themes/githelpers.theme +load ../../themes/base.theme + +add_commit() { + local file_name="general-${RANDOM}" + touch "${file_name}" + echo "" >> "${file_name}" + git add "${file_name}" + git commit -m"message" +} + +enter_new_git_repo() { + repo="$(setup_repo)" + pushd "${repo}" +} + +setup_repo() { + upstream="$(mktemp -d)" + pushd "$upstream" > /dev/null + git init . > /dev/null + + echo "$upstream" +} + +setup_repo_with_upstream() { + upstream="$(setup_repo)" + pushd "$upstream" > /dev/null + add_commit > /dev/null + git checkout -b branch-two + git checkout -b gone-branch + git checkout master + popd > /dev/null + + downstream="$(setup_repo)" + pushd "$downstream" > /dev/null + add_commit > /dev/null + git remote add my-remote "$upstream" + git fetch my-remote + git branch -u my-remote/master > /dev/null + popd > /dev/null + + pushd "$upstream" > /dev/null + git branch -d gone-branch > /dev/null + popd > /dev/null + + pushd "$downstream" > /dev/null + git fetch my-remote + popd > /dev/null + + echo "$downstream" +} + +@test 'themes base: Git: when tracking a remote branch: it shows the commits ahead and behind' { + pre="\$(_git-friendly-ref)" + + remote="$(setup_repo)" + pushd "$remote" + add_commit + add_commit + popd + + clone="$(mktemp -d)" + pushd "$clone" + git clone "$remote" clone + cd clone + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre}" + + add_commit + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre} ↑1" + + add_commit + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre} ↑2" + popd + + pushd "$remote" + add_commit + add_commit + add_commit + popd + + pushd "$clone/clone" + git fetch + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre} ↑2 ↓3" + + git reset HEAD~2 --hard + + SCM_GIT_BEHIND_CHAR="↓" + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre} ↓3" +} + +@test 'themes base: Git: when stashes exist: it shows the number of stashes' { + pre="\$(_git-friendly-ref)" + + enter_new_git_repo + add_commit + + touch file + git add file + git stash + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre} {1}" + + touch file2 + git add file2 + git stash + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre} {2}" +} + +@test 'themes base: Git: remote info: when there is no upstream remote: is empty' { + pre="\$(_git-friendly-ref)" + post=" ↑1 ↓1" + + enter_new_git_repo + add_commit + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre}" +} + +@test 'themes base: Git: remote info: when SCM_GIT_SHOW_REMOTE_INFO is true: includes the remote' { + pre="\$(_git-friendly-ref) → " + eval_pre="master → " + post=" ↑1 ↓1" + + repo="$(setup_repo_with_upstream)" + pushd "${repo}" + + SCM_GIT_SHOW_REMOTE_INFO=true + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre}my-remote${post}" + + git branch -u my-remote/branch-two + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre}\$(_git-upstream)${post}" + assert_equal "$(eval "echo \"$SCM_BRANCH\"")" "${eval_pre}my-remote/branch-two${post}" +} + +@test 'themes base: Git: remote info: when SCM_GIT_SHOW_REMOTE_INFO is auto: includes the remote when more than one remote' { + pre="\$(_git-friendly-ref)" + eval_pre="master" + post=" ↑1 ↓1" + + repo="$(setup_repo_with_upstream)" + pushd "${repo}" + + SCM_GIT_SHOW_REMOTE_INFO=auto + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre}${post}" + + pre="${pre} → " + eval_pre="${eval_pre} → " + git branch -u my-remote/branch-two + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre}\$(_git-upstream-branch)${post}" + assert_equal "$(eval "echo \"$SCM_BRANCH\"")" "${eval_pre}branch-two${post}" + + git remote add second-remote "$(mktemp -d)" + git branch -u my-remote/master + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre}my-remote${post}" + + git branch -u my-remote/branch-two + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre}\$(_git-upstream)${post}" + assert_equal "$(eval "echo \"$SCM_BRANCH\"")" "${eval_pre}my-remote/branch-two${post}" +} + +@test 'themes base: Git: remote info: when SCM_GIT_SHOW_REMOTE_INFO is false: never include the remote' { + pre="\$(_git-friendly-ref)" + eval_pre="master" + post=" ↑1 ↓1" + + repo="$(setup_repo_with_upstream)" + pushd "${repo}" + git remote add second-remote "$(mktemp -d)" + git remote add third-remote "$(mktemp -d)" + + SCM_GIT_SHOW_REMOTE_INFO=false + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre}${post}" + + pre="${pre} → " + eval_pre="${eval_pre} → " + git branch -u my-remote/branch-two + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre}\$(_git-upstream-branch)${post}" + assert_equal "$(eval "echo \"$SCM_BRANCH\"")" "${eval_pre}branch-two${post}" +} + +@test 'themes base: Git: remote info: when showing remote info: show if upstream branch is gone' { + pre="\$(_git-friendly-ref)" + post=" ↑1 ↓1" + + repo="$(setup_repo_with_upstream)" + pushd "${repo}" + + SCM_GIT_SHOW_REMOTE_INFO=true + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre} → my-remote${post}" + + git checkout gone-branch + git fetch --prune --all + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre} ⇢ my-remote" +} + +@test 'themes base: Git: git friendly ref: when a branch is checked out: shows that branch' { + enter_new_git_repo + + git_prompt_vars + assert_equal "$(eval "echo \"$SCM_BRANCH\"")" "master" + + git checkout -b second-branch + + git_prompt_vars + assert_equal "$(eval "echo \"$SCM_BRANCH\"")" "second-branch" +} + +@test 'themes base: Git: git friendly ref: when a branch is not checked out: shows that branch' { + enter_new_git_repo + + git_prompt_vars + assert_equal "$(eval "echo \"$SCM_BRANCH\"")" "master" + + git checkout -b second-branch + + git_prompt_vars + assert_equal "$(eval "echo \"$SCM_BRANCH\"")" "second-branch" +} + +@test 'themes base: Git: git friendly ref: when detached: commit has branch and tag: show a tag' { + enter_new_git_repo + add_commit + git tag first-tag + git checkout -b second-branch + add_commit + git checkout HEAD~1 + + git_prompt_vars + assert_equal "$(eval "echo \"$SCM_BRANCH\"")" "tag:first-tag" +} + +@test 'themes base: Git: git friendly ref: when detached: commit has branch and no tag: show a branch' { + enter_new_git_repo + add_commit + git checkout -b second-branch + add_commit + git checkout HEAD~1 + + git_prompt_vars + assert_equal "$(eval "echo \"$SCM_BRANCH\"")" "detached:master" +} + +@test 'themes base: Git: git friendly ref: when detached with no branch or tag: commit is parent to a named ref: show relative name' { + enter_new_git_repo + add_commit + add_commit + git checkout HEAD~1 + + git_prompt_vars + assert_equal "$(eval "echo \"$SCM_BRANCH\"")" "detached:master~1" +} + +@test 'themes base: Git: git friendly ref: when detached with no branch or tag: commit is not parent to a named ref: show short sha' { + enter_new_git_repo + add_commit + add_commit + sha="$(git rev-parse --short HEAD)" + git reset --hard HEAD~1 + git checkout "$sha" + + git_prompt_vars + assert_equal "$(eval "echo \"$SCM_BRANCH\"")" "detached:$sha" +} + +@test 'themes base: Git: git friendly ref: shows staged, unstaged, and untracked file counts' { + pre="\$(_git-friendly-ref)" + + enter_new_git_repo + echo "line1" > file1 + echo "line1" > file2 + echo "line1" > file3 + echo "line1" > file4 + git add . + git commit -m"commit1" + + git_prompt_vars + assert_equal "$SCM_STATE" " ✓" + + echo "line2" >> file1 + git add file1 + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre} S:1" + assert_equal "$SCM_STATE" " ✗" + assert_equal "$SCM_DIRTY" "3" + + echo "line2" >> file2 + echo "line2" >> file3 + echo "line2" >> file4 + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre} S:1 U:3" + assert_equal "$SCM_DIRTY" "2" + + echo "line1" > newfile5 + echo "line1" > newfile6 + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre} S:1 U:3 ?:2" + assert_equal "$SCM_DIRTY" "1" + + git config bash-it.hide-status 1 + + SCM_DIRTY='nope' + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre}" + assert_equal "$SCM_DIRTY" "nope" +} + +@test 'themes base: Git: git user info: shows user initials' { + pre="\$(_git-friendly-ref)" + + enter_new_git_repo + git config user.name "Cool User" + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre}" + + SCM_GIT_SHOW_CURRENT_USER=true + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre} ☺︎ cu" + + # show initials set by `git pair` + + git config user.initials "ab cd" + + git_prompt_vars + assert_equal "$SCM_BRANCH" "${pre} ☺︎ ab+cd" +} diff --git a/themes/base.theme.bash b/themes/base.theme.bash index a4dfabb0..b6106d00 100644 --- a/themes/base.theme.bash +++ b/themes/base.theme.bash @@ -129,156 +129,68 @@ function scm_prompt_info_common { [[ ${SCM} == ${SCM_SVN} ]] && svn_prompt_info && return } -# This is added to address bash shell interpolation vulnerability described -# here: https://github.com/njhartwell/pw3nage -function git_clean_branch { - local unsafe_ref=$(command git symbolic-ref -q HEAD 2> /dev/null) - local stripped_ref=${unsafe_ref##refs/heads/} - local clean_ref=${stripped_ref//[^a-zA-Z0-9\/]/-} - echo $clean_ref -} - function git_prompt_minimal_info { - local ref - local status - local git_status_flags=('--porcelain') SCM_STATE=${SCM_THEME_PROMPT_CLEAN} - if [[ "$(command git config --get bash-it.hide-status)" != "1" ]]; then - # Get the branch reference - ref=$(git_clean_branch) || \ - ref=$(command git rev-parse --short HEAD 2> /dev/null) || return 0 - SCM_BRANCH=${SCM_THEME_BRANCH_PREFIX}${ref} + _git-hide-status && return - # Get the status - [[ "${SCM_GIT_IGNORE_UNTRACKED}" = "true" ]] && git_status_flags+='-untracked-files=no' - status=$(command git status ${git_status_flags} 2> /dev/null | tail -n1) + SCM_BRANCH="${SCM_THEME_BRANCH_PREFIX}\$(_git-friendly-ref)" - if [[ -n ${status} ]]; then - SCM_DIRTY=1 - SCM_STATE=${SCM_THEME_PROMPT_DIRTY} - fi - - # Output the git prompt - SCM_PREFIX=${SCM_THEME_PROMPT_PREFIX} - SCM_SUFFIX=${SCM_THEME_PROMPT_SUFFIX} - echo -e "${SCM_PREFIX}${SCM_BRANCH}${SCM_STATE}${SCM_SUFFIX}" + if [[ -n "$(_git-status | tail -n1)" ]]; then + SCM_DIRTY=1 + SCM_STATE=${SCM_THEME_PROMPT_DIRTY} fi -} -function git_status_summary { - awk ' - BEGIN { - untracked=0; - unstaged=0; - staged=0; - } - { - if (!after_first && $0 ~ /^##.+/) { - print $0 - seen_header = 1 - } else if ($0 ~ /^\?\? .+/) { - untracked += 1 - } else { - if ($0 ~ /^.[^ ] .+/) { - unstaged += 1 - } - if ($0 ~ /^[^ ]. .+/) { - staged += 1 - } - } - after_first = 1 - } - END { - if (!seen_header) { - print - } - print untracked "\t" unstaged "\t" staged - }' + # Output the git prompt + SCM_PREFIX=${SCM_THEME_PROMPT_PREFIX} + SCM_SUFFIX=${SCM_THEME_PROMPT_SUFFIX} + echo -e "${SCM_PREFIX}${SCM_BRANCH}${SCM_STATE}${SCM_SUFFIX}" } function git_prompt_vars { - local details='' + if _git-branch &> /dev/null; then + SCM_GIT_DETACHED="false" + SCM_BRANCH="${SCM_THEME_BRANCH_PREFIX}\$(_git-friendly-ref)$(_git-remote-info)" + else + SCM_GIT_DETACHED="true" + + local detached_prefix + if _git-tag &> /dev/null; then + detached_prefix=${SCM_THEME_TAG_PREFIX} + else + detached_prefix=${SCM_THEME_DETACHED_PREFIX} + fi + SCM_BRANCH="${detached_prefix}\$(_git-friendly-ref)" + fi + + IFS=$'\t' read -r commits_behind commits_ahead <<< "$(_git-upstream-behind-ahead)" + [[ "${commits_ahead}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_GIT_AHEAD_CHAR}${commits_ahead}" + [[ "${commits_behind}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_GIT_BEHIND_CHAR}${commits_behind}" + + local stash_count + stash_count="$(git stash list 2> /dev/null | wc -l | tr -d ' ')" + [[ "${stash_count}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_GIT_STASH_CHAR_PREFIX}${stash_count}${SCM_GIT_STASH_CHAR_SUFFIX}" + SCM_STATE=${GIT_THEME_PROMPT_CLEAN:-$SCM_THEME_PROMPT_CLEAN} - if [[ "$(git config --get bash-it.hide-status)" != "1" ]]; then - [[ "${SCM_GIT_IGNORE_UNTRACKED}" = "true" ]] && local git_status_flags='-uno' - local status_lines=$((git status --porcelain ${git_status_flags} -b 2> /dev/null || - git status --porcelain ${git_status_flags} 2> /dev/null) | git_status_summary) - local status=$(awk 'NR==1' <<< "$status_lines") - local counts=$(awk 'NR==2' <<< "$status_lines") - IFS=$'\t' read untracked_count unstaged_count staged_count <<< "$counts" + if ! _git-hide-status; then + IFS=$'\t' read -r untracked_count unstaged_count staged_count <<< "$(_git-status-counts)" if [[ "${untracked_count}" -gt 0 || "${unstaged_count}" -gt 0 || "${staged_count}" -gt 0 ]]; then SCM_DIRTY=1 if [[ "${SCM_GIT_SHOW_DETAILS}" = "true" ]]; then - [[ "${staged_count}" -gt 0 ]] && details+=" ${SCM_GIT_STAGED_CHAR}${staged_count}" && SCM_DIRTY=3 - [[ "${unstaged_count}" -gt 0 ]] && details+=" ${SCM_GIT_UNSTAGED_CHAR}${unstaged_count}" && SCM_DIRTY=2 - [[ "${untracked_count}" -gt 0 ]] && details+=" ${SCM_GIT_UNTRACKED_CHAR}${untracked_count}" && SCM_DIRTY=1 + [[ "${staged_count}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_GIT_STAGED_CHAR}${staged_count}" && SCM_DIRTY=3 + [[ "${unstaged_count}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_GIT_UNSTAGED_CHAR}${unstaged_count}" && SCM_DIRTY=2 + [[ "${untracked_count}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_GIT_UNTRACKED_CHAR}${untracked_count}" && SCM_DIRTY=1 fi SCM_STATE=${GIT_THEME_PROMPT_DIRTY:-$SCM_THEME_PROMPT_DIRTY} fi fi - [[ "${SCM_GIT_SHOW_CURRENT_USER}" == "true" ]] && details+="$(git_user_info)" - - SCM_CHANGE=$(git rev-parse --short HEAD 2>/dev/null) - - local ref=$(git_clean_branch) - - if [[ -n "$ref" ]]; then - SCM_BRANCH="${SCM_THEME_BRANCH_PREFIX}${ref}" - local tracking_info="$(grep -- "${SCM_BRANCH}\.\.\." <<< "${status}")" - if [[ -n "${tracking_info}" ]]; then - [[ "${tracking_info}" =~ .+\[gone\]$ ]] && local branch_gone="true" - tracking_info=${tracking_info#\#\# ${SCM_BRANCH}...} - tracking_info=${tracking_info% [*} - local remote_name=${tracking_info%%/*} - local remote_branch=${tracking_info#${remote_name}/} - local remote_info="" - local num_remotes=$(git remote | wc -l 2> /dev/null) - [[ "${SCM_BRANCH}" = "${remote_branch}" ]] && local same_branch_name=true - if ([[ "${SCM_GIT_SHOW_REMOTE_INFO}" = "auto" ]] && [[ "${num_remotes}" -ge 2 ]]) || - [[ "${SCM_GIT_SHOW_REMOTE_INFO}" = "true" ]]; then - remote_info="${remote_name}" - [[ "${same_branch_name}" != "true" ]] && remote_info+="/${remote_branch}" - elif [[ ${same_branch_name} != "true" ]]; then - remote_info="${remote_branch}" - fi - if [[ -n "${remote_info}" ]];then - if [[ "${branch_gone}" = "true" ]]; then - SCM_BRANCH+="${SCM_THEME_BRANCH_GONE_PREFIX}${remote_info}" - else - SCM_BRANCH+="${SCM_THEME_BRANCH_TRACK_PREFIX}${remote_info}" - fi - fi - fi - SCM_GIT_DETACHED="false" - else - local detached_prefix="" - ref=$(git describe --tags --exact-match 2> /dev/null) - if [[ -n "$ref" ]]; then - detached_prefix=${SCM_THEME_TAG_PREFIX} - else - ref=$(git describe --contains --all HEAD 2> /dev/null) - ref=${ref#remotes/} - [[ -z "$ref" ]] && ref=${SCM_CHANGE} - detached_prefix=${SCM_THEME_DETACHED_PREFIX} - fi - SCM_BRANCH=${detached_prefix}${ref} - SCM_GIT_DETACHED="true" - fi - - local ahead_re='.+ahead ([0-9]+).+' - local behind_re='.+behind ([0-9]+).+' - [[ "${status}" =~ ${ahead_re} ]] && SCM_BRANCH+=" ${SCM_GIT_AHEAD_CHAR}${BASH_REMATCH[1]}" - [[ "${status}" =~ ${behind_re} ]] && SCM_BRANCH+=" ${SCM_GIT_BEHIND_CHAR}${BASH_REMATCH[1]}" - - local stash_count="$(git stash list 2> /dev/null | wc -l | tr -d ' ')" - [[ "${stash_count}" -gt 0 ]] && SCM_BRANCH+=" ${SCM_GIT_STASH_CHAR_PREFIX}${stash_count}${SCM_GIT_STASH_CHAR_SUFFIX}" - - SCM_BRANCH+=${details} + [[ "${SCM_GIT_SHOW_CURRENT_USER}" == "true" ]] && SCM_BRANCH+="$(git_user_info)" SCM_PREFIX=${GIT_THEME_PROMPT_PREFIX:-$SCM_THEME_PROMPT_PREFIX} SCM_SUFFIX=${GIT_THEME_PROMPT_SUFFIX:-$SCM_THEME_PROMPT_SUFFIX} + + SCM_CHANGE=$(_git-short-sha 2>/dev/null || echo "") } function svn_prompt_vars { diff --git a/themes/githelpers.theme.bash b/themes/githelpers.theme.bash new file mode 100644 index 00000000..e6bda810 --- /dev/null +++ b/themes/githelpers.theme.bash @@ -0,0 +1,157 @@ +#!/usr/bin/env bash + +function _git-symbolic-ref { + git symbolic-ref -q HEAD 2> /dev/null +} + +# When on a branch, this is often the same as _git-commit-description, +# but this can be different when two branches are pointing to the +# same commit. _git-branch is used to explicitly choose the checked-out +# branch. +function _git-branch { + git symbolic-ref -q --short HEAD 2> /dev/null || return 1 +} + +function _git-tag { + git describe --tags --exact-match 2> /dev/null +} + +function _git-commit-description { + git describe --contains --all 2> /dev/null +} + +function _git-short-sha { + git rev-parse --short HEAD +} + +# Try the checked-out branch first to avoid collision with branches pointing to the same ref. +function _git-friendly-ref { + _git-branch || _git-tag || _git-commit-description || _git-short-sha +} + +function _git-num-remotes { + git remote | wc -l +} + +function _git-upstream { + local ref + ref="$(_git-symbolic-ref)" || return 1 + git for-each-ref --format="%(upstream:short)" "${ref}" +} + +function _git-upstream-remote { + local upstream + upstream="$(_git-upstream)" || return 1 + + local branch + branch="$(_git-upstream-branch)" || return 1 + echo "${upstream%"/${branch}"}" +} + +function _git-upstream-branch { + local ref + ref="$(_git-symbolic-ref)" || return 1 + + # git versions < 2.13.0 do not support "strip" for upstream format + # regex replacement gives the wrong result for any remotes with slashes in the name, + # so only use when the strip format fails. + git for-each-ref --format="%(upstream:strip=3)" "${ref}" 2> /dev/null || git for-each-ref --format="%(upstream)" "${ref}" | sed -e "s/.*\/.*\/.*\///" +} + +function _git-upstream-behind-ahead { + git rev-list --left-right --count "$(_git-upstream)...HEAD" 2> /dev/null +} + +function _git-upstream-branch-gone { + [[ "$(git status -s -b | sed -e 's/.* //')" == "[gone]" ]] +} + +function _git-hide-status { + [[ "$(git config --get bash-it.hide-status)" == "1" ]] +} + +function _git-status { + [[ "${SCM_GIT_IGNORE_UNTRACKED}" = "true" ]] && local git_status_flags='-uno' + git status --porcelain ${git_status_flags} 2> /dev/null +} + +function _git-status-counts { + _git-status | awk ' + BEGIN { + untracked=0; + unstaged=0; + staged=0; + } + { + if ($0 ~ /^\?\? .+/) { + untracked += 1 + } else { + if ($0 ~ /^.[^ ] .+/) { + unstaged += 1 + } + if ($0 ~ /^[^ ]. .+/) { + staged += 1 + } + } + } + END { + print untracked "\t" unstaged "\t" staged + }' +} + +function _git-remote-info { + [[ "$(_git-upstream)" == "" ]] && return + + [[ "$(_git-branch)" == "$(_git-upstream-branch)" ]] && local same_branch_name=true + if ([[ "${SCM_GIT_SHOW_REMOTE_INFO}" = "auto" ]] && [[ "$(_git-num-remotes)" -ge 2 ]]) || + [[ "${SCM_GIT_SHOW_REMOTE_INFO}" = "true" ]]; then + if [[ "${same_branch_name}" != "true" ]]; then + remote_info="\$(_git-upstream)" + else + remote_info="$(_git-upstream-remote)" + fi + elif [[ ${same_branch_name} != "true" ]]; then + remote_info="\$(_git-upstream-branch)" + fi + if [[ -n "${remote_info}" ]];then + local branch_prefix + if _git-upstream-branch-gone; then + branch_prefix="${SCM_THEME_BRANCH_GONE_PREFIX}" + else + branch_prefix="${SCM_THEME_BRANCH_TRACK_PREFIX}" + fi + echo "${branch_prefix}${remote_info}" + fi +} + +# Unused by bash-it, present for API compatibility +function git_status_summary { + awk ' + BEGIN { + untracked=0; + unstaged=0; + staged=0; + } + { + if (!after_first && $0 ~ /^##.+/) { + print $0 + seen_header = 1 + } else if ($0 ~ /^\?\? .+/) { + untracked += 1 + } else { + if ($0 ~ /^.[^ ] .+/) { + unstaged += 1 + } + if ($0 ~ /^[^ ]. .+/) { + staged += 1 + } + } + after_first = 1 + } + END { + if (!seen_header) { + print + } + print untracked "\t" unstaged "\t" staged + }' +}