From c336acfc57bd408dcb93379a171270612c3f1cd3 Mon Sep 17 00:00:00 2001 From: Matt Hunter Date: Wed, 6 May 2026 03:31:13 -0400 Subject: Revert status output to a single prompt element Output for changes staged/unstaged/unmerged/untracked are combined to a single prompt element %{status}, like it was prior to the recent rewrite (though it used to be called 'changes'). The main reason for this is to address script performance. The sed calls in prepare_element start to add up, and having fewer of them helps improve runtimes. The ability to customize the prompt via a flexible format string is a really nice feature that I'd like to keep, so I'm reducing the number of substitutable elements in an effort to balance performance. Reverting this specific change is one of the easier decisions to make in that regard. Furthermore, the actual element_status implementation is updated to rely on fewer external calls, additionally aiding performance. Now that the output is controlled by a single element, the arrangement of the different change stages is no longer fine-tunable via the prompt format string. A fixed order is produced: staged, unmerged, unstaged, untracked - exactly as it was before the recent rewrite. Within the output, these stages are separated by a "status separator" string (as before), however this separator is now exposed to the user as a configurable environment variable. Signed-off-by: Matt Hunter --- git-sonar | 88 ++++++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/git-sonar b/git-sonar index 7d71606..06fcf1e 100755 --- a/git-sonar +++ b/git-sonar @@ -83,6 +83,7 @@ FETCH_TIME="${GIT_SONAR_FETCH_TIME:-"300"}" ALERT_ICON="${GIT_SONAR_ALERT_ICON:-"${COLOR_YELLOW}⚡"}" STASH_ICON="${GIT_SONAR_STASH_ICON:-"${COLOR_YELLOW}≡"}" BRANCH_COLOR="${GIT_SONAR_BRANCH_COLOR:-"$COLOR_DEF"}" +STATUS_SEP="${GIT_SONAR_STATUS_SEPARATOR:-" "}" AHEAD_ICON="${GIT_SONAR_AHEAD_ICON:-"${COLOR_GREEN}↑"}" # "←" BEHIND_ICON="${GIT_SONAR_BEHIND_ICON:-"${COLOR_RED}↓"}" # "→" @@ -94,12 +95,9 @@ UNMERGED_COLOR="${GIT_SONAR_UNMERGED_COLOR:-"$COLOR_YELLOW"}" UNTRACKED_COLOR="${GIT_SONAR_UNTRACKED_COLOR:-"$COLOR_WHITE"}" PROMPT_COLOR="${GIT_SONAR_PROMPT_COLOR:-"$COLOR_GRAY"}" -PROMPT_FORMAT="${GIT_SONAR_PROMPT_FORMAT:-" ${PROMPT_COLOR}git:(${COLOR_DEF}%{alert}%{remote: }%{branch}%{ :local}${PROMPT_COLOR})${COLOR_DEF}%{ :stash}%{ :staged}%{ :unmerged}%{ :unstaged}%{ :untracked}"}" +PROMPT_FORMAT="${GIT_SONAR_PROMPT_FORMAT:-" ${PROMPT_COLOR}git:(${COLOR_DEF}%{alert}%{remote: }%{branch}%{ :local}${PROMPT_COLOR})${COLOR_DEF}%{ :stash}%{ :status}"}" -# Gather current git status and branch information. git porcelain status can -# be especially expensive to compute, so bypass it if the prompt disables status -# information. -[ -n "$opt_status" ] && git_status="$(git status --porcelain 2>/dev/null)" || git_status="" +# Gather information about the current git branch. upstream_name="$(git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' 2>/dev/null)" branch_name="$(git symbolic-ref --short HEAD 2>/dev/null)" commit_hash="$(git rev-parse --short HEAD 2>/dev/null)" @@ -123,11 +121,6 @@ line_count() { wc -l 2>/dev/null | grep -oE '[1-9][0-9]*' } -status_count() { - # Use a sed transform to prevent unmerged paths from being miscounted - echo "$git_status" | sed -nE "s/^AA /AU /;s/^DD /DU /;/${1}/p" | line_count -} - print_commit_range() { if [ -n "$1" ] && [ -n "$2" ]; then ahead="$(git rev-list --count "${1}..${2}" 2>/dev/null)" || ahead="0" @@ -179,41 +172,65 @@ element_stash() { fi } -element_staged() { +element_status() { if [ -n "$opt_status" ]; then + # git status is the most expensive subprocess we call, so place it here + # to bypass it if $opt_status is "false". The output is filtered + # through sed to prevent certain unmerged paths from being double + # counted as staged/unstaged ("AA"/"DD"). + git_status="$(\ + git status --porcelain 2>/dev/null \ + | sed 's/^AA /AU /;s/^DD /DU /' \ + )" + + gs="" + st_sep="$STATUS_SEP" + um_sep="$STATUS_SEP" + us_sep="$STATUS_SEP" + ut_sep="$STATUS_SEP" + + # See "man 1 git-status" sections "Short Format" and "Porcelain Format + # Version 1" for an explanation of these status indicators. + + # Staged for x in A M R C D T; do - if cnt="$(status_count "^${x}[^U] ")"; then - printf '%s%b%s%b' "$cnt" "$STAGED_COLOR" "$x" "$COLOR_DEF" + if cnt="$(echo "$git_status" | grep -cE "^${x}[^U] ")"; then + gs="$(printf '%b%b%s%b%s%b' \ + "$gs" "$st_sep" "$cnt" "$STAGED_COLOR" "$x" "$COLOR_DEF" \ + )" + st_sep="" fi done - fi -} -element_unstaged() { - if [ -n "$opt_status" ]; then - for x in M D T; do - if cnt="$(status_count "^[^U]${x} ")"; then - printf '%s%b%s%b' "$cnt" "$UNSTAGED_COLOR" "$x" "$COLOR_DEF" + # Unmerged, conflicted + for x in A U D; do + if cnt="$(echo "$git_status" | grep -cE "^(U${x}|${x}U) ")"; then + gs="$(printf '%b%b%s%b%s%b' \ + "$gs" "$um_sep" "$cnt" "$UNMERGED_COLOR" "$x" "$COLOR_DEF" \ + )" + um_sep="" fi done - fi -} -element_unmerged() { - if [ -n "$opt_status" ]; then - for x in A U D; do - if cnt="$(status_count "^(U${x}|${x}U) ")"; then - printf '%s%b%s%b' "$cnt" "$UNMERGED_COLOR" "$x" "$COLOR_DEF" + # Unstaged + for x in M D T; do # R C omitted + if cnt="$(echo "$git_status" | grep -cE "^[^U]${x} ")"; then + gs="$(printf '%b%b%s%b%s%b' \ + "$gs" "$us_sep" "$cnt" "$UNSTAGED_COLOR" "$x" "$COLOR_DEF" \ + )" + us_sep="" fi done - fi -} -element_untracked() { - if [ -n "$opt_status" ]; then - if cnt="$(status_count "^\?\? ")"; then - printf '%s%b?%b' "$cnt" "$UNTRACKED_COLOR" "$COLOR_DEF" + # Untracked + if cnt="$(echo "$git_status" | grep -cE "^\?\? ")"; then + gs="$(printf '%b%b%s%b?%b' \ + "$gs" "$ut_sep" "$cnt" "$UNTRACKED_COLOR" "$COLOR_DEF" \ + )" + ut_sep="" fi + + printf '%b' "${gs#"$STATUS_SEP"}" fi } @@ -255,7 +272,4 @@ printf '%b' "$PROMPT_FORMAT" | sed \ -e "$(prepare_element remote element_remote)" \ -e "$(prepare_element local element_local)" \ -e "$(prepare_element stash element_stash)" \ - -e "$(prepare_element staged element_staged)" \ - -e "$(prepare_element unstaged element_unstaged)" \ - -e "$(prepare_element unmerged element_unmerged)" \ - -e "$(prepare_element untracked element_untracked)" + -e "$(prepare_element status element_status)" -- cgit v1.2.3