summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Hunter <m@lfurio.us>2026-05-06 03:31:13 -0400
committerMatt Hunter <m@lfurio.us>2026-05-08 03:53:48 -0400
commitc336acfc57bd408dcb93379a171270612c3f1cd3 (patch)
tree07c393c2e25f02a3c2c1ad2bc252c41a669427f8
parent5f7de1d0b89d9d5cc6f437061a62e38721077667 (diff)
downloadgit-sonar-c336acfc57bd408dcb93379a171270612c3f1cd3.tar.gz
git-sonar-c336acfc57bd408dcb93379a171270612c3f1cd3.zip
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 <m@lfurio.us>
Diffstat (limited to '')
-rwxr-xr-xgit-sonar88
1 files 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)"