Git stash doesn't have to be scary
It was only when I recently heard someone else say, “git stash is scary” that I realized it was top of my list of fears too. It just usually feels like there’s a chance I’ll just lose all of my in progress work to the depths of stashes and never be able to get the right incantation of git
commands to reincarnate my code.
What does git stash do?
Let me backtrack a minute! I haven’t yet explained what git stash
even does. On the most basic level, git stash
is one way to store in progress work in git
for subsequent access, leaving behind a clean working directory.
Meta-note: this post will be easiest to follow along if you open up a terminal and play around with git stash
as you’re reading! The lack of syntax highlighting makes it a little tricky to see git additions / deletions. You can play around with any git repo you have, or clone this hello world one locally to work in a complete sandbox!
Stacks
Stashes are more easily understood as a basic last in, first out stack.
git stash push
(equivalent togit stash
when used with no arguments) will push any local changes onto the stash and leave a clean working directory.
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
$ git stash push
Saved working directory and index state WIP on main: 6ab43aa Initial commit
$ git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
There are a plethora of arguments we can pass to git stash push
. The ones I find most helpful are:
-m <message>
saves the stash with a message, can be helpful for our future selves
$ git stash push -m "description"
Saved working directory and index state On main: description
-p
patches changes (used likegit add -p
), allowing us to select which hunks to add to the stash. Hunks are areas where two files differ. Git will partition files into hunks for us!
$ git stash push -p
....
Details about the hunk
....
(1/1) Stash this hunk [y,n,q,a,d,e,?]?
-u
includes untracked files in the stash. Without this flag, a file thatgit
had never seen before would not be included in a stash
$ touch new-file
$ git status
On branch main
Your branch is up to date with 'origin/main'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
new-file
nothing added to commit but untracked files present (use "git add" to track)
$ git stash push -u
Saved working directory and index state WIP on main: 6ab43aa Initial commit
-k
keeps the files (or hunks) which were staged. Without this option, when we apply a stash, nothing will be staged. More on applying in the next section!
Accessing the stash
Okay, we can now push changes onto the stash. But…. they’re still the stash abyss if we have no way of accessing them.
git stash list
shows us what is on the stash:
$ git stash list
stash@{0}: On main: Clear description of these changes
stash@{1}: WIP on main: 6ab43aa Initial commit
This is where the -m
message flag from earlier will be incredibly helpful. The first item in my stash had a message (Clear description of these changes
), the second did not.
Okay, okay. We can now see what’s on the stash. But how do we access anything on the stash? Or remove it from the stash?
git stash show
will show us the difference between the top item on the stash, and our current working directory:
$ git stash show
README.md | 1 +
1 file changed, 1 insertion(+)
If we want to reference a different set of changes on the stash, and not the most recent one, we can use the stash index of those changes. As we can see above, each item on the stash has an index, and can be referred to by stash@{index}
So, to see the set of changes in the next item on our stack, we can run:
$ git stash show stash@{1}
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Neat, so we now know how to get the differences and look at what’s in the stash. But to actually access what’s on the stash, we’ll need git stash apply [<stash>]
. It similarly can take an argument with the stash index to reference something later in the stash. If no argument is provided, it will default to the most recent set of changes we put in the stash (the ones at stash@{0}
)
$ git stash apply stash@{1}
On branch main
Your branch is up to date with 'origin/main'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
Removing from the stash
However, git stash apply
will not remove the changes from the stash. If we look at the stash now, we’ll see it’s the same as it was:
$ git stash list
stash@{0}: On main: Clear description of these changes
stash@{1}: WIP on main: 6ab43aa Initial commit
In order to remove a set of changes from the stash, we can use git stash drop [<stash>]
$ git stash drop stash@{0}
Dropped refs/stash@{0} (b09812812eb27de2cb49bfd02aba43889362b6de)
$ git stash list
stash@{0}: WIP on main: 6ab43aa Initial commit
We can see that the set of changes at stash@{0}
are now no longer on the stash, and the set of changes which were at stash@{1}
are now at stash@{0}
.
If we want to apply
and drop
a set of changes, we can conveniently use git stash pop [<stash>]
. pop
will only also drop
if there’s no merge conflict.
To clear the stash completely, we can run git stash clear
. This is the actual scary command which will cause us to lose any work in progress that you had stashed. Only run git stash clear
if you never again want to access what is currently on the stash.
Why stash?
So we’ve learned how to use git stash
, but still haven’t covered why to use it. Stashing is intended to be a quick and easy way to get to a clean working state while not losing in progress work. And it is! Both quick, and now hopefully easy!
One example of when I might use git stash
is if I’m in progress on some changes, but realize I want to amend a prior commit. I can stash the changes, amend the commit, and then apply the stash to get back to where I was.
Another is if I am trying to experiment with a few different ways to solve a problem, and want small, quick examples down each solution path. I might create stashes with all of my examples, and then pick the best and pursue that set of changes in a branch structure.
Additionally, stashing is especially helpful because it is branch agnostic. That is to say, if we switch branches, our stash will remain the same. This means stashing can also be a convenient way to carry changes through different branches.
When not to stash
Equally import to why stash is when not to use it.
Stashing was built for convenience, and is not intended to be a permanent way to save work; that’s what branches and commits are for. Stashing also only works locally, you can’t push stashes to a repo or have others pull them down.
Branches from stash
Speaking of branches, another fun feature of stashes is that we can create a branch directly from a stash. This is done using git stash branch <branch_name> [<stash>]
This will also drop the changes from the stash, equivalent to a git stash pop
not a git stash apply
.
$ git stash branch new-branch
Switched to a new branch 'new-branch'
On branch new-branch
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (d6a804a40c0b477d8e3be27939310ff677f45670)
In summary
git stash
operates on a LIFO stack of sets of changesgit stash list
shows what’s on the stashgit stash push
pushes to the stashgit stash apply
access changes on the stashgit stash drop
removes changes from the stashgit stash pop
does anapply
and adrop
- These default to the most recent item on the stash, to reference another one use
stash@{index}
where you can determine the index fromgit stash list
- Some of these have flags which make them more convenient, including:
-m
message,-p
patch,-u
include untracked files,-k
keep staged changes staged
Hopefully you, too, can now add git stash
to the list of fears you’ve conquered!
For further reading on stashing, take a look at the git stash docs. For further reading on conquering fears, here is a favorite quote of mine, “Nothing in life is to be feared, it is only to be understood. Now is the time to understand more, so that we may fear less.” - Marie Curie