From 45a954cb0f8b67e35529efbcdf60f0621d95f4a1 Mon Sep 17 00:00:00 2001 From: Billy Conn Date: Thu, 13 Oct 2016 22:31:44 -0500 Subject: [PATCH 1/2] git_prompt_status now uses hash lookups instead of multiple greps --- lib/git.zsh | 124 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 91 insertions(+), 33 deletions(-) diff --git a/lib/git.zsh b/lib/git.zsh index b3b25f802..578f319e4 100644 --- a/lib/git.zsh +++ b/lib/git.zsh @@ -147,46 +147,104 @@ function git_prompt_long_sha() { SHA=$(__git_prompt_git rev-parse HEAD 2> /dev/null) && echo "$ZSH_THEME_GIT_PROMPT_SHA_BEFORE$SHA$ZSH_THEME_GIT_PROMPT_SHA_AFTER" } -# Get the status of the working tree function git_prompt_status() { - emulate -L zsh - [[ "$(__git_prompt_git config --get oh-my-zsh.hide-status 2>/dev/null)" = 1 ]] && return - local INDEX STATUS - INDEX=$(__git_prompt_git status --porcelain -b 2> /dev/null) || return 0 - STATUS="" - if [[ "${INDEX}" =~ $'(^|\n)\\?\\? ' ]]; then - STATUS="$ZSH_THEME_GIT_PROMPT_UNTRACKED$STATUS" - fi - if [[ "${INDEX}" =~ $'(^|\n)(A |M |MM) ' ]]; then - STATUS="$ZSH_THEME_GIT_PROMPT_ADDED$STATUS" - fi - if [[ "${INDEX}" =~ $'(^|\n)([ AM]M| T) ' ]]; then - STATUS="$ZSH_THEME_GIT_PROMPT_MODIFIED$STATUS" - fi - if [[ "${INDEX}" =~ $'(^|\n)R ' ]]; then - STATUS="$ZSH_THEME_GIT_PROMPT_RENAMED$STATUS" - fi - if [[ "${INDEX}" =~ $'(^|\n)([A ]D|D ) ' ]]; then - STATUS="$ZSH_THEME_GIT_PROMPT_DELETED$STATUS" + local status_prompt="" + + # A lookup table of each git status encountered + local -A statuses_seen + + # Maps a git status prefix to an internal constant + # This cannot use the prompt constants, as they may be empty + local -A prefix_constant_map=( + '?? ' 'UNTRACKED' + 'A ' 'ADDED' + 'M ' 'ADDED' + 'MM ' 'ADDED' + ' M ' 'MODIFIED' + 'AM ' 'MODIFIED' + ' T ' 'MODIFIED' + 'R ' 'RENAMED' + ' D ' 'DELETED' + 'D ' 'DELETED' + 'UU ' 'UNMERGED' + 'ahead' 'AHEAD' + 'behind' 'BEHIND' + 'diverged' 'DIVERGED' + 'stashed' 'STASHED' + ) + + # Maps the internal constant to the prompt theme + local -A constant_prompt_map=( + 'UNTRACKED' "$ZSH_THEME_GIT_PROMPT_UNTRACKED" + 'ADDED' "$ZSH_THEME_GIT_PROMPT_ADDED" + 'MODIFIED' "$ZSH_THEME_GIT_PROMPT_MODIFIED" + 'RENAMED' "$ZSH_THEME_GIT_PROMPT_RENAMED" + 'DELETED' "$ZSH_THEME_GIT_PROMPT_DELETED" + 'UNMERGED' "$ZSH_THEME_GIT_PROMPT_UNMERGED" + 'AHEAD' "$ZSH_THEME_GIT_PROMPT_AHEAD" + 'BEHIND' "$ZSH_THEME_GIT_PROMPT_BEHIND" + 'DIVERGED' "$ZSH_THEME_GIT_PROMPT_DIVERGED" + 'STASHED' "$ZSH_THEME_GIT_PROMPT_STASHED" + ) + + # The order that the prompt displays should be added to the prompt + local status_constants=(UNTRACKED ADDED MODIFIED RENAMED DELETED STASHED + UNMERGED AHEAD BEHIND DIVERGED) + + local status_text=$(__git_prompt_git status --porcelain -b 2> /dev/null) + + # Don't continue on a catastrophic failure + if [[ $? -eq 128 ]]; then + return 1 fi + if $(__git_prompt_git rev-parse --verify refs/stash >/dev/null 2>&1); then - STATUS="$ZSH_THEME_GIT_PROMPT_STASHED$STATUS" + statuses_seen['STASHED']=1 fi - if [[ "${INDEX}" =~ $'(^|\n)UU ' ]]; then - STATUS="$ZSH_THEME_GIT_PROMPT_UNMERGED$STATUS" + + local status_lines=("${(@f)${status_text}}"); + + # If the tracking line exists, get and parse it + if [[ $status_lines[1] =~ "^## [^ ]+ \[(.*)\]" ]]; then + local branch_statuses=("${(@s/,/)match}") + for branch_status in $branch_statuses; do + if [[ ! $branch_status =~ "(behind|diverged|ahead) ([0-9]+)?" ]]; then + continue + fi + local last_parsed_status=$prefix_constant_map[$match[1]] + statuses_seen[$last_parsed_status]=$match[2] + done + shift status_lines fi - if [[ "${INDEX}" =~ $'(^|\n)## [^ ]\+ .*ahead' ]]; then - STATUS="$ZSH_THEME_GIT_PROMPT_AHEAD$STATUS" - fi - if [[ "${INDEX}" =~ $'(^|\n)## [^ ]\+ .*behind' ]]; then - STATUS="$ZSH_THEME_GIT_PROMPT_BEHIND$STATUS" - fi - if [[ "${INDEX}" =~ $'(^|\n)## [^ ]\+ .*diverged' ]]; then - STATUS="$ZSH_THEME_GIT_PROMPT_DIVERGED$STATUS" - fi - echo $STATUS + + # This not only gives us a status lookup, but the count of each type + for status_line in ${status_lines}; do + local status_prefix=${status_line[1, 3]} + local status_constant=${(v)prefix_constant_map[$status_prefix]} + + if [[ -z $status_constant ]]; then + continue + fi + + (( statuses_seen[$status_constant]++ )) + done + + # At this point, the statuses_seen hash contains: + # - Tracking => The difference between tracked and current + # - Modifications => The count of that type of modification + # - Stash => Whether or not a stash exists + # Might be useful for someone? + + for status_constant in $status_constants; do + if [[ ${+statuses_seen[$status_constant]} -eq 1 ]]; then + local next_display=$constant_prompt_map[$status_constant] + status_prompt="$next_display$status_prompt" + fi + done + + echo $status_prompt } # Outputs the name of the current user From 865f6572d586e7ba1231333cadf808002688603e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Cornell=C3=A0?= Date: Sat, 14 Apr 2018 23:37:45 +0200 Subject: [PATCH 2/2] Fix performance drop of iterating over lines and other stuff - Fix 'STASHED' key and unsupported syntax in 5.0.2 - Optimise `if` statement to make it more idiomatic. --- lib/git.zsh | 62 ++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/lib/git.zsh b/lib/git.zsh index 578f319e4..f9b27949e 100644 --- a/lib/git.zsh +++ b/lib/git.zsh @@ -150,15 +150,11 @@ function git_prompt_long_sha() { function git_prompt_status() { [[ "$(__git_prompt_git config --get oh-my-zsh.hide-status 2>/dev/null)" = 1 ]] && return - local status_prompt="" - - # A lookup table of each git status encountered - local -A statuses_seen - # Maps a git status prefix to an internal constant # This cannot use the prompt constants, as they may be empty - local -A prefix_constant_map=( - '?? ' 'UNTRACKED' + local -A prefix_constant_map + prefix_constant_map=( + '\?\? ' 'UNTRACKED' 'A ' 'ADDED' 'M ' 'ADDED' 'MM ' 'ADDED' @@ -176,7 +172,8 @@ function git_prompt_status() { ) # Maps the internal constant to the prompt theme - local -A constant_prompt_map=( + local -A constant_prompt_map + constant_prompt_map=( 'UNTRACKED' "$ZSH_THEME_GIT_PROMPT_UNTRACKED" 'ADDED' "$ZSH_THEME_GIT_PROMPT_ADDED" 'MODIFIED' "$ZSH_THEME_GIT_PROMPT_MODIFIED" @@ -190,25 +187,33 @@ function git_prompt_status() { ) # The order that the prompt displays should be added to the prompt - local status_constants=(UNTRACKED ADDED MODIFIED RENAMED DELETED STASHED - UNMERGED AHEAD BEHIND DIVERGED) + local status_constants + status_constants=( + UNTRACKED ADDED MODIFIED RENAMED DELETED + STASHED UNMERGED AHEAD BEHIND DIVERGED + ) - local status_text=$(__git_prompt_git status --porcelain -b 2> /dev/null) + local status_text="$(__git_prompt_git status --porcelain -b 2> /dev/null)" # Don't continue on a catastrophic failure if [[ $? -eq 128 ]]; then return 1 fi - if $(__git_prompt_git rev-parse --verify refs/stash >/dev/null 2>&1); then - statuses_seen['STASHED']=1 + # A lookup table of each git status encountered + local -A statuses_seen + + if __git_prompt_git rev-parse --verify refs/stash &>/dev/null; then + statuses_seen[STASHED]=1 fi - local status_lines=("${(@f)${status_text}}"); + local status_lines + status_lines=("${(@f)${status_text}}") # If the tracking line exists, get and parse it - if [[ $status_lines[1] =~ "^## [^ ]+ \[(.*)\]" ]]; then - local branch_statuses=("${(@s/,/)match}") + if [[ "$status_lines[1]" =~ "^## [^ ]+ \[(.*)\]" ]]; then + local branch_statuses + branch_statuses=("${(@s/,/)match}") for branch_status in $branch_statuses; do if [[ ! $branch_status =~ "(behind|diverged|ahead) ([0-9]+)?" ]]; then continue @@ -216,29 +221,22 @@ function git_prompt_status() { local last_parsed_status=$prefix_constant_map[$match[1]] statuses_seen[$last_parsed_status]=$match[2] done - shift status_lines fi - # This not only gives us a status lookup, but the count of each type - for status_line in ${status_lines}; do - local status_prefix=${status_line[1, 3]} - local status_constant=${(v)prefix_constant_map[$status_prefix]} + # For each status prefix, do a regex comparison + for status_prefix in ${(k)prefix_constant_map}; do + local status_constant="${prefix_constant_map[$status_prefix]}" + local status_regex="(^|\n)$status_prefix" - if [[ -z $status_constant ]]; then - continue + if [[ "$status_text" =~ $status_regex ]]; then + statuses_seen[$status_constant]=1 fi - - (( statuses_seen[$status_constant]++ )) done - # At this point, the statuses_seen hash contains: - # - Tracking => The difference between tracked and current - # - Modifications => The count of that type of modification - # - Stash => Whether or not a stash exists - # Might be useful for someone? - + # Display the seen statuses in the order specified + local status_prompt for status_constant in $status_constants; do - if [[ ${+statuses_seen[$status_constant]} -eq 1 ]]; then + if (( ${+statuses_seen[$status_constant]} )); then local next_display=$constant_prompt_map[$status_constant] status_prompt="$next_display$status_prompt" fi