131
votes

I recently started using Zsh and it's awesome. Unfortunately, for the project I consider my "main" project, everything is slow. What I mean is that every time I run a command - ls, for example - there's about a five-second delay between the time the command is executed and the time I can use the terminal again.

What could be different about this one repo that makes Zsh so slow? I assume it's a Zsh-specific thing because there was no problem before I started using Zsh. I tried doing a git clean but it didn't make any noticeable difference.

I'm on Mac OS X if that matters.

Update: Turns out this line of my .zshenv is what was making it slow:

[[ -s "$HOME/.rvm/scripts/rvm" ]] && . "$HOME/.rvm/scripts/rvm" # Load RVM function

If I comment that line, it goes from taking about 3 seconds to taking about 1 second. Unfortunately, I need that line, since many of my projects use RVM. I don't know what to do now.

Update 2: this seems to be specifically an oh-my-zsh thing. If I don't load ~/.oh-my-zsh/oh-my-zsh.sh, I don't have a problem.

10
and if you use bash now, is it still slow?nneonneo
Excellent question. No, it's fast on bash.Jason Swett
Is it still slow if you move your ~/.z* files out of the way?user4815162342
No. So I guess it's something in one of those.Jason Swett
If the repo is big, calling git st everytime might slow the shell down. Use this option to turn it off. git config --add oh-my-zsh.hide-status 1Senthil Kumar

10 Answers

241
votes

You can add this to your git config and zsh won't check the status anymore

git config --add oh-my-zsh.hide-status 1
git config --add oh-my-zsh.hide-dirty 1

Explanation

There are two central git functions in in lib/git.zsh:

  • git_prompt_info()
  • parse_git_dirty()

Each Method has a git config switch to disable it:

  • oh-my-zsh.hide-status
  • oh-my-zsh.hide-dirty

Some themes create their own git queries and sometimes ignore these flags.

32
votes

Oh_my_zsh seems to be slow for some repos because it checks the status of the repo after each command. This behaviour can be overridden in the new version of .oh_my_zsh . Just Uncomment the following line in .zshrc:

DISABLE_UNTRACKED_FILES_DIRTY="true"

After this, restart your terminal or run the following:

source ~/.zshrc

19
votes

It could be the theme calling git and rvm stuff after every command.

For me, changing ZSH_THEME="juanghurtadoto" to ZSH_THEME="miloshadzic" removed the 2 second delay after every command completely.

Themes can be found at https://github.com/robbyrussell/oh-my-zsh/wiki/themes

15
votes

For me it's slow on VirtualBox (the guest) because I'm using a synced folder. I still want it enabled on OS X (the host) where it's fast enough. Instead of using a local config setting which is stored with the repo and would change it both on the guest and host, I use a global config setting only on the guest:

git config --global --add oh-my-zsh.hide-dirty 1

If I want it just for a single repo:

git config --add oh-my-zsh.hide-dirty 1
13
votes

There are various way to speed up an oh-my-zsh, as detailed in "zsh starts incredibly slowly", cleaning up the plugin section.

For instance, the blog post "Fix for oh-my-zsh git-svn prompt slowness" mentions the parse_git_dirty function as a potential issue.

6
votes

I finally figured it out. My project had a rake folder with a ton of files (like 20,000). I have no idea what that folder was there for, but I deleted it, Zsh is no longer slow, and my app still seems to work.

3
votes

If you don't care about another version control programs but git, you can just disable all vcs_infos in your *.zsh-theme and replace them with native git commands.

For example I tweak my agnoster.zsh-theme by:

  1. comment/remove all lines that have vcs_info,
  2. edit code in prompt_git() function from:

    ref="$vcs_info_msg_0_" to

    ref="$(git branch 2>/dev/null | grep -Po '(?<=\* ).*$')"

So, basically, I just disabled all vcs_info actions, and instead use a native git command for checking statuses of the repo. As a result, I still can see my useful git prompt with a speed as fast as working in a non-git directory. With this small modification, my zsh can work 4-5x faster within git repos.

Note: use GNU grep not BSD grep.

1
votes

Answers above didn't solve my problem. In my case, the function git_prompt_status takes too much time than others. So I modified ~/.oh-my-zsh/lib/git.zsh, replacing git_prompt_status function with my early return version:

function git_prompt_status() {
  STATUS="$ZSH_THEME_GIT_PROMPT_MODIFIED$STATUS"
  echo $STATUS
  return

  local INDEX STATUS
  INDEX=$(command git status --porcelain -b 2> /dev/null)
  STATUS=""
  if $(echo "$INDEX" | command grep -E '^\?\? ' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_UNTRACKED$STATUS"
  fi
  if $(echo "$INDEX" | grep '^A  ' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_ADDED$STATUS"
  elif $(echo "$INDEX" | grep '^M  ' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_ADDED$STATUS"
  elif $(echo "$INDEX" | grep '^MM ' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_ADDED$STATUS"
  fi
  if $(echo "$INDEX" | grep '^ M ' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_MODIFIED$STATUS"
  elif $(echo "$INDEX" | grep '^AM ' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_MODIFIED$STATUS"
  elif $(echo "$INDEX" | grep '^MM ' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_MODIFIED$STATUS"
  elif $(echo "$INDEX" | grep '^ T ' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_MODIFIED$STATUS"
  fi
  if $(echo "$INDEX" | grep '^R  ' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_RENAMED$STATUS"
  fi
  if $(echo "$INDEX" | grep '^ D ' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_DELETED$STATUS"
  elif $(echo "$INDEX" | grep '^D  ' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_DELETED$STATUS"
  elif $(echo "$INDEX" | grep '^AD ' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_DELETED$STATUS"
  fi
  if $(command git rev-parse --verify refs/stash >/dev/null 2>&1); then
    STATUS="$ZSH_THEME_GIT_PROMPT_STASHED$STATUS"
  fi
  if $(echo "$INDEX" | grep '^UU ' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_UNMERGED$STATUS"
  fi
  if $(echo "$INDEX" | grep '^## [^ ]\+ .*ahead' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_AHEAD$STATUS"
  fi
  if $(echo "$INDEX" | grep '^## [^ ]\+ .*behind' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_BEHIND$STATUS"
  fi
  if $(echo "$INDEX" | grep '^## [^ ]\+ .*diverged' &> /dev/null); then
    STATUS="$ZSH_THEME_GIT_PROMPT_DIVERGED$STATUS"
  fi
  echo $STATUS
}

While I use ZSH_THEME_GIT_PROMPT_MODIFIED as a mark of undetermined, you may choose any status you like to indicate that or implement a much faster git_prompt_status function in your case.

1
votes

For anyone using the spaceship theme add this to .zshrc:

SPACESHIP_GIT_STATUS_SHOW="false"

worked for me. Obviously, you will lose the git status in your terminal prompt.

The git options for this theme can be found here

Edits

Updated link to git options is here

0
votes

For others coming to this question looking to improve their zsh git latency, the following reduced my latency from 40ms to 4ms:

  1. Compile and install an optimized git-branch-name command:

    git clone https://github.com/notfed/git-branch-name
    cd git-branch-name
    make
    sudo install git-branch-name /usr/local/bin/
    
  2. Add this to your ~/.zshrc:

    function git_prompt_info() {
        ref=$(git-branch-name -q -h 12 -b 64) || return
        echo "${ZSH_THEME_GIT_PROMPT_PREFIX}${ref}${ZSH_THEME_GIT_PROMPT_CLEAN}${ZSH_THEME_GIT_PROMPT_SUFFIX}"
    }
    

(The -h and -b flags control truncation length for branch names and hashes, respectively.)

With this, I can hold enter and not experience any lag.