Skip to content

Instantly share code, notes, and snippets.

@davidcelis
Last active May 23, 2023 07:41
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidcelis/01e9ff680e8d66a4eab704576a72eb59 to your computer and use it in GitHub Desktop.
Save davidcelis/01e9ff680e8d66a4eab704576a72eb59 to your computer and use it in GitHub Desktop.
Watch for changes in a slow monorepo and update a `git status` cache for your prompt to read from 😬

A git prompt hack for the impatient amongst us

Maybe you have a big ol' monorepo, and git operations are pretty slow in it. For those of us who like to show information about the repository's dirty status in our prompt, that means a slow prompt. Waiting 1s every time you hit the return key can really add up! Instead of just giving up and eschewing the dirty status entirely, why not just cache it in the background have your prompt read from the cache?

Prerequisites

First, install fswatch:

brew install fswatch

Also, if your monorepo doesn't come with a tmp/ or log/ directory, quickly create those first just in case:

cd /path/to/monorepo
mkdir -p tmp
mkdir -p log

Install

Copy the contents of watch-for-git-changes script and install it in /usr/local/bin:

pbpaste > /usr/local/bin/watch-for-git-changes
chmod +x /usr/local/bin/watch-for-git-changes

Then, copy the contents of the plist and install it in your LaunchAgents directory:

pbpaste > ~/Library/LaunchAgents/com.$USER.update-monorepo-git-dirty.plist

Replace any instances of USER with your system username, and replace /path/to/monorepo with... well... the path to your monorepo.

Finally, load the launchagent so it starts running:

launchctl load ~/Library/LaunchAgents/com.$USER.update-pay-server-git-dirty.plist

To verify that it's running correctly, check its status:

launchctl list | grep "com.$USER"

If it's actually running and hasn't exited, the first value will be a PID instead of empty. Try modifying a file in the your monorepo that's tracked by git. After you do so, there should now be a cache of the change at /path/to/monorepo/tmp/.git_dirty.

If launchctl list is showing something bad or your tempfile isn't being created, hopefully the log file at /path/to/monorepo/log/update-git-dirty.log says something. If not, feel free to ping me on Twitter for help.

Usage

Now that you have that .git_dirty file, you can use it to report on there being changes from your prompt. For example, this is what my git prompt looks like now (and, yes, it's fish. sorry not sorry! 🐟):

# The function you want to look at is `_git_dirty`

function __fish_prompt_git
  set -g __git_branch_name (command git symbolic-ref --short -q HEAD ^/dev/null)

  if not set -q -g __git_functions_defined
    set -g __git_functions_defined

    function _git_dirty
      if test -e $PWD/tmp/.git_dirty
        echo (cat $PWD/tmp/.git_dirty)
      else
        echo (command git status -s --ignore-submodules=dirty)
      end
    end

    function _git_ahead
      echo (command git log --format=oneline origin/$__git_branch_name..$__git_branch_name ^/dev/null)
    end

    function _git_behind
      echo (command git log --format=oneline $__git_branch_name..origin/$__git_branch_name ^/dev/null)
    end
  end

  set -l cyan   (set_color -o cyan)
  set -l yellow (set_color -o yellow)
  set -l purple (set_color -o purple)
  set -l blue   (set_color -o blue)
  set -l normal (set_color normal)

  set -l cwd $cyan(prompt_pwd)

  if [ $__git_branch_name ]
    set -l git_branch $purple$__git_branch_name
    set git_info "$blue $git_branch"

    if [ (_git_dirty) ]
      set -l dirty $yellow!
      set git_info $git_info$dirty
    end

    if [ (_git_ahead) ]
      set -l ahead $blue↑
      set git_info $git_info$ahead
    end

    if [ (_git_behind) ]
      set -l behind $blue↓
      set git_info $git_info$behind
    end
  end

  echo $git_info
end

This results in a prompt that looks like this when there are new changes in tracked files (or if there are new untracked files):

prompt

All the relevant function does now is check for the existence of a file at $PWD/tmp/.git_dirty and read from it. If it's not there, it'll run git status on the fly.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.USER.update-monorepo-git-dirty</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/usr/local/bin:/usr/bin:/bin</string>
</dict>
<key>Program</key>
<string>/usr/local/bin/watch-for-git-changes</string>
<key>WorkingDirectory</key>
<string>/path/to/monorepo</string>
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>/path/to/monorepo/log/update-git-dirty.log</string>
<key>StandardErrorPath</key>
<string>/path/to/monorepo/log/update-git-dirty.log</string>
</dict>
</plist>
#!/usr/bin/env bash
fswatch -re \.git* $PWD | while read file; do
git check-ignore -q $file && continue;
git status -s --ignore-submodules=dirty > $PWD/tmp/.git_dirty;
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment