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).