2

I use git diff --exit-code to color my shell prompt so that I can see if there are diffs in the working copy. On very large repositories, this takes a very long time, as long as git diff without the extra argument.

I had hoped that --exit-code would cause git to exit as soon as it found the first difference, but in fact, it actually prints the diff. When run with --quiet, it of course doesn't print the diff, but still takes equally long, which suggests it's still computing all the changes, even if not showing them.

So my question is: is there a way with git diff (or any other git command) to simply check whether there are any diffs, and set an exit code accordingly, but without bothering to find and format all the actual changes?

Edit: For context on the size of the repo, it's about 7.5G (of which 4.1G is the .git/ dir), and ~160k tracked files.

2 Answers 2

5

As far as I know, there isn't any way to do this directly with git.

However, you can use the following shell script fragment to check much more quickly:

#!/bin/sh
[ `git status --porcelain=1 | wc -l` -ne 0 ] && exit 1

What this does is get machine parseable output from the git status command (which runs faster than git diff on large repositories because it is only checking for the presence of changes, not what they are), counting the number of lines (the --porcelain=1 format will return exactly one line for each modified file, and no lines if none are modified), and then exiting with a non-zero exit status if the number is non-zero.

You could also just embed this directly in whatever script is generating your prompt by pulling out the conditional expression (the bit in the []) and using that in whatever if statement is checking the exit code for git diff --quiet currently.

0

I can't immediately find a simple command, but I did check to see what the git prompt module in oh-my-zsh does, and it is reasonably fast:

Check the output of git status --porcelain --untracked-files=no. When a file is modified, it will have an M in the second column, and when a file conflict is present, it will have a U in the first column.

You can do something like this:

prompt() {
    git status --porcelain --untracked-files=no | grep '^.M' > /dev/null
    CHANGED=$?
    # $CHANGED -eq 0 when there are uncommitted changes

    git status --porcelain --untracked-files=no | grep '^U.' > /dev/null
    CONFLICTS=$?
    # $CONFLICTS -eq 0 when there are merge or rebase conflicts

    git status --porcelain --untracked-files=no | grep '^' > /dev/null
    CLEAN=$((1 - $?))
    # $CLEAN -eq 0 when there are *no* changes, conflicts, or uncommitted staged files
}
1
  • Unfortunately this seems to take about as long as the original git diff I posted above.
    – dcrosta
    Commented Dec 6, 2017 at 19:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .