Clean working directory without getting your hands dirty - The story of Git, part six

Hi there!

Welcome back to another blog post from my series about git. Make sure to check them out if you're interested in Git, how it works, how to fix some mess if you end up in it, and so on. This one will be about the stash. Mind you, everything is legal here - we're talking about git stash.

Feel free to share this article if you liked it, forward it to friends if you received them via e-mail, and you can also add comments or feedback below.


What it is?

As with everything, we'll start from the beginning - What is git stash? It is a way of cleaning your working directory without committing anything, but recording it nevertheless.

But what does that mean?

Imagine your working directory being your table. On that table, you have a big pile of books. To commit those books would be to put them on a shelf, however, to stash them would mean to put them beneath the table and return to them when you need them. They are still there and you can get them a lot easier.

How does it work underneath?

A stash entry itself in Git is represented as a commit that records the working directory, with two parents - the first one being the HEAD when the entry was created and the second parent records the state of the index when the entry was made, and that parent also becomes a child of a HEAD commit.

This is what the ancestry graph looks like.

      /    /

The H here is the HEAD commit, I is the commit recording the state of the index, and W is the commit that records the working directory.

The latest stash which was created is stored in refs/stash, and the older ones are located in the reflog. Make sure you keep this in mind when we go through the problem I write about at the end.

When can you use it?

Some of the use cases of git stash are as follows:

  • Pulling into a dirty working directory - you are in the middle of something and you learn that the upstream changes impact yours; you want to pull, but Git doesn't allow you, because of a conflict, now what? Well, you can use git stash, to stash your working directory elsewhere (we saw above), pull in the latest changes, and bring back the stashed ones.
  • Interrupted workflow - somebody asks you to do just this quick fix, even though you are in the middle of everything. Since you are a good person, you will accept, and before creating a quick fix - git stash to the rescue!
  • Testing partial commits - you want to test something out, but you don't want to include some change yet - no problem, stage those changes you need to, use git stash to put other (currently unwanted) changes away, and voila - you can continue on.
  • Saving unrelated changes for future use - wow, this code looks marvelous, you can definitely use it at some point. Not now, however, it's to mature. Well, git stash, and make sure you don't forget about it.

How does it look in practice?

The following code snippet shows the basic usage of git stash - create entry, list entry, return entry, and delete an entry.

## Creating a stash
$ git stash
Saved working directory and index state WIP on main; d7435844 Fix: re-configure graphql endpoint
## Listing stash
$ git stash list
stash@{0}: WIP on main: d7435844 Fix: re-configure graphql endpoint
## Retrieving entry
git stash apply stash@{0}
### Or you can use
$ git stash pop stash@{0} 
### The difference between pop and apply is that pop will delete the stash entry when it's applied
## Deleting a stash
$ git stash drop stash@{0}

I accidentally dropped the stash, how to bring it back?

This actually happened to me last month. I made some changes locally and didn't want to commit them yet, but there were some changes in the upstream that I wanted. So, I stashed everything, pulled upstream to clean the working directory, and instead of popping I dropped it.

My first thought - Oh shit! My second thought - somebody already had this issue, why don't I look it up? And there it was. In short - you search the objects in the Git database with the following command.

git fsck --no-reflog | awk '/dangling commit/ {print $3}'

This will print out hashes that are dangling. Fortunately, this list wasn't so big for my repository, so I went through each commit one by one until I found the one I dropped by mistake. I applied it and everything seemed so right again...

The Wrap-Up

Congrats, you've reached the end of yet another blog post. I hope you liked it! Below you can find some links I found useful when researching the git stash topic.

Thanks and see you in the next blog post!