‘git stash pop’ considered harmful
Git has a number of features designed to ease development hassle. One oft-mentioned example is
git stash, which allows you to take any uncommitted changes and “stash them away.”
After changes have been stashed, there are a few options of how to get them back:
git stash poptakes a stashed change, removes it from the “stash stack”, and applies it to your current working tree.
git stash applytakes a stashed change and applies it to your current working tree (also leaving it on the “stash stack”).
git stash branchcreates a new branch from the same commit you were on when you stashed the changes, and applies the stashed changes to that new branch.
Those who begin using stashing tend to just use the first option,
pop – after all, stashing is designed to reduce development hassle and
pop, which cleans up the stash you probably don’t care about anymore, has the least hassle involved. Right?
pop saves you a
git stash drop after you’ve re-applied your stashed changes… some of the time.
For instance, say your stashed changes conflict with other changes that you’ve made since you first created the stash. Both
apply will helpfully trigger merge conflict resolution mode, allowing you to nicely resolve such conflicts… and neither will get rid of the stash, even though perhaps you’re expecting
pop to. Since a lot of people expect stashes to just be a simple stack, this often leads to them popping the same stash accidentally later because they thought it was gone.
It’s quite possible to avoid this by simply using
git stash apply consistently instead of
git stash pop.
(A side note: if you apply a stash and it conflicts with an already staged-change, you can get the originally staged version of the file back via
git checkout --ours <path>. This can be handy if you forget to commit before failing to apply a stash.)
I’d go one step further and suggest another option for your consideration: don’t use stashing at all. One of Git’s biggest strengths is that commits and branches are cheap. Instead of creating stashes, why not just create a new branch and commit your changes on it? There are many reasons to use real branches instead of stashes:
- You can always get rid of the branch later after merging or cherry-picking your changes off it.
- Your changes are always in the context in which they were created (since they have branch history).
- It’s harder to forget about things that show up in the branch list (I know lots of people who forget about stashes).
- You can easily swap to that branch if you think of some more things you want to add to your saved changes, and then swap back.
- Creating commits gives you more impetus to actually associate a message with them, making it easier to remember what you were doing.
Plus, you can get near stash-like functionality via commits and branches with a Git alias:
[alias] save = !sh -c 'export PREV=$(git symbolic-ref HEAD|cut -d/ -f3-) && git checkout -b "$1" && git commit -am "$1" && git checkout "$PREV"' -
With this git alias, you can do
git save foobar and it will:
- Create a branch named “foobar”
- Commit any changes on that branch
- Swap you back to the branch you started on
All with a single command, just like
git stash does, but with none of the drawbacks of the stash system.