Stashing Changes in Git
There you are. Sitting at your desk, sipping your favorite coffee from your favorite mug, coding away on a new project. You’re proud of your progress so far and are just about to make your first commit.
And that’s when you realize. Oh !@#$. You’ve been working in the wrong branch.
What do you do? Delete all of your work and start over? Manually duplicate the changed files somewhere else on your computer and then undo the changes? Ugh, what a mess.
The ideal situation would be temporarily saving your changes within Git, switching to the correct branch, and then applying those changes back again.
With git-stash
this is possible. Let’s look how it works.
First things first. We need to stash our changes.
git stash save "javascript fixes for bug 3829"
And then we want to create and check out the proper branch:
$ git branch javascript-bug-fix-3829 && git checkout javascript-bug-fix-3829
Now that we’re on the proper branch, we can re-apply our work in progress, which is stored as a stash in Git.
$ git stash apply stash@{0}
Okay, that’s a lot there. Let’s step back and learn more about Stash.
How Stash Works #
Stash takes changes in a “dirty working directory” — a directory that has changes in it that haven’t yet been staged or committed — and saves them off in a special way so you can easily retrieve them and apply them back to another working directory.
After you run git stash
, it takes the changes out of the working directory, saves them (as a git commit object, actually) and then returns the branch to the state of the HEAD commit.
A stash is a commit object just like any other commit but the difference is that the branch HEAD isn’t pointed at them.
Let’s dig in and take a look at stashes as they’re stored in our .git
directory.
$ cd .git/refs && more stash
03ed3f4cdc26a09b6d7b3974cdfc2ba8977c3d77
What is returned is SHA1 hash of the changes for this stash. This SHA1 hash points to a commit but it’s treated as a stash because it is stored in the .git/refs/stash
file.
We can see what stashes we have saved using
$ git stash list
That will return a list of stashes with a stash id, branch, and then any message we saved with the stash.
stash@{0}: On develop: updated the offline file
stash@{1}: On develop: testing out git stash
stash@{2}: WIP on master: 4fd1101 fixing layout on homepage product listing
stash@{3}: On develop: product bundle download template
To demonstrate that a stash is just a normal Git commit object treated differently, let’s retrieve the same list but using git-log.
$ git log --format="%gd: %gs" -g --first-parent -m "$@" stash@{0} --
stash@{0}: On develop: updated the offline file
stash@{1}: On develop: testing out git stash
stash@{2}: WIP on master: 4fd1101 fixing layout on homepage product listing
stash@{3}: On develop: product bundle download template
Exactly the same output.
So, where did I get that git-log idea from? Well, it turns out that git-stash is just a shell script that access some of the “plumbing and porcelain” of Git to enable us to use Git commit objects as stash items.
It does take away from the magical feeling of using git-stash, however, it’s pretty darn cool that Git can reuse the commit objects in so many different ways.
Helpful Stash Commands #
You can dig into the Git shell script for Stash to learn everything about it but in lieu of that let’s review a few really handy options with git-stash.
git stash pop
In the earlier example we use git stash apply
to apply a saved stash to our working directory. When you use the apply
option, Git will keep the stash saved even after you apply it. If this is too messy — or potentially confusing — to you, you can use a different option to apply the stash and then have it removed.
$ git stash pop stash@{0}
This also applies the stash to the current working tree but it removes the stash from the list.
Note: The most recent stash is always the one with the index of 0. So, when you remove apply and remove a stash using git stash pop
, a stash with index of 0 will still be there but it’ll point to a different commit object.
git stash drop
If you want to remove a stash completely from your stash list (handy for regular housecleaning), you can do that with:
$ git stash drop stash@{4}
If you don’t specify which stash to specify
$ git stash drop
then it’ll always drop the latest one, which would stash@{0}
.
If you really need to clean house in your list of stashes, you can wipe them all out at once.
$ git stash clear
git stash branch
Let’s revisit our earlier scenario where we started working on the wrong branch and needed to stash our changes, create a new branch, and then apply the stash.
Git provides us with an option to do that all with one command.
$ git stash branch javascript-bug-fix-3829
This command creates a new branch at the HEAD where the stash was created, checks that branch out, applies the stash to it, and then deletes the stash. We just saved a lot of typing. Just like with most stash commands, if we don’t specify a stash it will always use the last one saved.