浏览代码

git_prompt_status now uses hash lookups instead of multiple greps

Billy Conn 7 年之前
父节点
当前提交
45a954cb0f
共有 1 个文件被更改,包括 91 次插入33 次删除
  1. 91 33
      lib/git.zsh

+ 91 - 33
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"
-  fi
-  if [[ "${INDEX}" =~ $'(^|\n)UU ' ]]; then
-    STATUS="$ZSH_THEME_GIT_PROMPT_UNMERGED$STATUS"
+    statuses_seen['STASHED']=1
   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"
+
+  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
-  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