Stashing Changes in Git

There you are. Sit­ting at your desk, sip­ping your favorite cof­fee from your favorite mug, cod­ing 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 real­ize. Oh !@#$. You’ve been work­ing in the wrong branch.

What do you do? Delete all of your work and start over? Man­u­al­ly dupli­cate the changed files some­where else on your com­put­er and then undo the changes? Ugh, what a mess.

The ide­al sit­u­a­tion would be tem­porar­i­ly sav­ing your changes with­in Git, switch­ing to the cor­rect branch, and then apply­ing those changes back again.

With git-stash this is pos­si­ble. 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 cre­ate and check out the prop­er branch:

$ git branch javascript-bug-fix-3829 && git checkout javascript-bug-fix-3829

Now that we’re on the prop­er 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 work­ing direc­to­ry” — a direc­to­ry that has changes in it that haven’t yet been staged or com­mit­ted — and saves them off in a spe­cial way so you can eas­i­ly retrieve them and apply them back to anoth­er work­ing directory.

After you run git stash, it takes the changes out of the work­ing direc­to­ry, saves them (as a git com­mit object, actu­al­ly) and then returns the branch to the state of the HEAD commit.

A stash is a com­mit object just like any oth­er com­mit but the dif­fer­ence is that the branch HEAD isn’t point­ed at them.

Let’s dig in and take a look at stash­es as they’re stored in our .git directory.

$ cd .git/refs && more stash

What is returned is SHA1 hash of the changes for this stash. This SHA1 hash points to a com­mit but it’s treat­ed as a stash because it is stored in the .git/refs/stash file.

We can see what stash­es we have saved using

$ git stash list

That will return a list of stash­es with a stash id, branch, and then any mes­sage 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 demon­strate that a stash is just a nor­mal Git com­mit object treat­ed dif­fer­ent­ly, 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

Exact­ly 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 plumb­ing and porce­lain” of Git to enable us to use Git com­mit objects as stash items.

It does take away from the mag­i­cal feel­ing of using git-stash, how­ev­er, it’s pret­ty darn cool that Git can reuse the com­mit objects in so many dif­fer­ent ways.

Help­ful Stash Com­mands #

You can dig into the Git shell script for Stash to learn every­thing about it but in lieu of that let’s review a few real­ly handy options with git-stash.

git stash pop

In the ear­li­er exam­ple we use git stash apply to apply a saved stash to our work­ing direc­to­ry. When you use the apply option, Git will keep the stash saved even after you apply it. If this is too messy — or poten­tial­ly con­fus­ing — to you, you can use a dif­fer­ent option to apply the stash and then have it removed.

$ git stash pop stash@{0}

This also applies the stash to the cur­rent work­ing 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 dif­fer­ent com­mit object.

git stash drop

If you want to remove a stash com­plete­ly from your stash list (handy for reg­u­lar house­clean­ing), you can do that with:

$ git stash drop stash@{4}

If you don’t spec­i­fy which stash to specify

$ git stash drop

then it’ll always drop the lat­est one, which would stash@{0}.

If you real­ly need to clean house in your list of stash­es, you can wipe them all out at once. 

$ git stash clear

git stash branch

Let’s revis­it our ear­li­er sce­nario where we start­ed work­ing on the wrong branch and need­ed to stash our changes, cre­ate a new branch, and then apply the stash.

Git pro­vides us with an option to do that all with one command.

$ git stash branch javascript-bug-fix-3829

This com­mand cre­ates a new branch at the HEAD where the stash was cre­at­ed, checks that branch out, applies the stash to it, and then deletes the stash. We just saved a lot of typ­ing. Just like with most stash com­mands, if we don’t spec­i­fy a stash it will always use the last one saved.