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