Check if Git has changes programmatically

bash, git

Solution

`git stash` is actually a shell (plain sh, not bash) script, so you can look at what it does to decide whether to make a stash:

no_changes () {
        git diff-index --quiet --cached HEAD --ignore-submodules -- &&
        git diff-files --quiet --ignore-submodules &&
        (test -z "$untracked" || test -z "$(untracked_files)")
}

untracked_files () {
        excl_opt=--exclude-standard
        test "$untracked" = "all" && excl_opt=
        git ls-files -o -z $excl_opt
}

You can therefore repeat these tests to see if `git stash` will do anything—but that's actually not the best option:

- If `git stash` will do something, first it repeats these tests (and they say there is something to stash), which makes them happen twice in that case.

- If the "right" set of tests ever change, you can mis-predict `git stash`'s action and get into trouble.

Clearly, what you want is to:

- Run `git stash`, and keep track of whether this did anything.

- Run `git checkout` on the target branch.

- If `git stash` stashed something, run `git stash pop` (or perhaps `--apply --keep-index` first, to see if that will do the trick, and resort to applying without `--keep-index` only if required, etc.).

The tricky part is that first step. It turns out to be easy, you just need to know how `git stash` works in, um, extensive detail, plus some other bits about git's "plumbing commands". You simply grab the previous stash stack top if any before the `git stash save` step, and then grab the new top if any, and compare:

old_stash=$(git rev-parse -q --verify refs/stash)
git stash save [additional arguments here if desired, e.g., -q]
new_stash=$(git rev-parse -q --verify refs/stash)

If the two refs name different commits, or the old ref is empty and the new is not empty, then `git stash` pushed a new stash on the stack. If the two refs are both empty, or the two refs name the same commit, then `git stash` did nothing:

if [ "$old_stash" = "$new_stash" ]; then ...; else ...; fi

or, in your case, perhaps something more like:

[ "$old_stash" != "$new_stash" ] && git stash pop

(these tests make use of the fact that the two empty strings are equal to each other, and not-equal to any non-empty string, in terms of the result from `/bin/[` and the shell's built-in copy thereof).

Problem

I want to write a script that allows me to easily change branches keeping my current changes. ``` git stash go checkout main git stash pop ``` The problem is if there are currently no changes in the repository. Then `stash pop` will pop off a previous stash. What is the easiest way to check if there are changes to pop?

Original source

Related problems