Git stashes are a lightweight, and fast, way to save the current state of your working directory when you need to temporarily switch contexts without losing your work. This is great for saving experimental code spikes, exploring ideas, and so forth.
In the past, stashes has been private and local to your machine but with the release of Git 2.51.0 that is no longer true. We’ll explore all of this in more detail but, first, let’s get setup so we can explore further.
Setup
For the purposes of discussion, we’ll use a demo project for exploring how stashes work. Here’s how to get started (assuming your are using GitHub, adjust if otherwise):
# Local Setup
mkdir demo
cd demo
git init
# Remote Setup
git remote add origin https://github.com/$USER/demo.git
git branch -M main
git push -u origin main
The above is all you need to start exploring stashes. If you don’t care about importing/exporting your stashes, then you can ignore the remote setup.
Configuration
There are a few tweaks you’ll want to make to your global confugration order to make effect using of your stashes. The first is — and for those that heavily use Git Rebase (highly recommend) — you’ll want to enable Git Rebase AutoStash as it’ll vastly improve your workflow. Additionally, you’ll want to update your global configuration as follows:
# Ensures untracked changes are included when showing a stash.
git config set --global stash.showIncludeUntracked true
# Ensures the stash is shown in patch form.
git config set --global stash.showPatch true
# Disables showing stash stats so you can customize the format instead.
git config set --global stash.showStat false
# Enables remote tracking for exporting and importing of your stashes.
git config set --global remote.origin.fetch 'refs/stashes/*:refs/remote/origin/stashes/*'
💡 For more on maximizing the effectivess of Git, check out my Git Configuration article to learn more.
Push
By default, you only need to use git stash to push changes onto your stash. Example:
touch one.txt
git stash
The above will push all staged and unstaged changes onto your stash as "WIP on <branch>" for the message. The "WIP on <branch>" message, much like a bad commit message, isn’t terribly helpful once you push multiple changes onto your stash and see they all share the same message. A better way to handle this is to switch to push and provide a distinct message. Example:
git stash push --include-untracked --message "A demonstration"
The above pushes all changes onto your stash by including unstaged changes which is why the --include-untracked flag is included and then finally ends with the --message option to supply a distinct message to help you remember what your change is about. An even better way is to wrap the above in a Bash function in your Dotfiles as follows:
# Label: Git Stash
# Description: Creates stash of all changes.
# Parameters: $1 (optional): Label. Default: "All Work (YYYY-MM-DD HH:MM:SS AM|PM Z)."
gash() {
local label=${1:-"All Work ($(date '+%Y-%m-%d %r %Z'))."}
git stash push --include-untracked --message "$label"
}
Now you can simply type gash with or without a message (to default to a timestamped message) or supply your own unique message that best helps you remember.
List
Once you’ve pushed a few stashes, you can list all stashes by using git stash list to see them all. For example, you might see something like this:
stash@{0}: On main: All Work (2025-09-06 06:00:00 PM MDT)
stash@{1}: On main: A demonstration
stash@{2}: On main: An asynchronous processing code spike
While the aboe useful, we can do better by adding color via Git Log Pretty Formatting:
git stash list --pretty=format:'%C(magenta)%gd%C(reset) %C(yellow)%h%C(reset) %s %C(green)(%cr)%C(reset)'
What the above is doing is listing your stashes with pretty formatting by:
-
Using magenta to color the stash shortened reflog selector (i.e.
%gd) with a color reset after. -
Using yellow to color the abbreviated hash (i.e.
%h) with a color reset after. -
Using green to color the committer date in relative time with a color reset after.
By adding a splash of color to your output, you improve readability.
Show
Sometimes you might want to view a specific stash instead of all of them at once. To do this, you can use git stash show as follows:
git show stash stash@{0}
Use of stash@{0} is your stash reflog reference which you can obtain when listing your stashes. By default, this is not always enough information. To improve the inspection of your stashes, either of the following is recommended:
# Shows a single stash by including untracked changes.
git stash show --include-untracked stash@{0}
# Shows a single stash by using your configured diff tool.
git difftool stash@{0}
With the above you can view staged and unstaged changes or view changes as configured via your specific difftool (assuming you are using an external diff tool).
Pop
Once you’ve pushed a stash, you can restore the stash by popping it as follows:
git stash pop stash@{0}
You only need to switch the reflog reference index (i.e. 0) to the specific index you want to pop from the stash. Doing this will restore a previous stash so you can return to work.
Drop
Dropping a stash is the equivalent of deleting it. This works the same as popping but will permanently delete your stash. Example:
git stash drop stash@{0}
Once again, you’d adjust the index for the specific stash you want to drop.
Clear
In situations where you have pushed multiple stashes and want to delete all of them without having to individually drop them, then you can clear them in a single command. Example:
git stash clear
Export
Prior to Git 2.51.0, you could not share your stashes with others but is no longer the case. Example:
git stash export --to-ref "refs/stashes/$USER";
With the above, you’ll export your entire stash to the refs/stashes/$USER reference. In this case, $USER would equate to whatever your operating system user is so you have a nice way to dynamically name your stash and have it tied to your identity. If you don’t want $USER, then you could use "personal", "work", or whatever reference name is most comfortable.
Being able to export your stashes allows you to share work with other but also ensure your stashes aren’t lost should you delete your repository and re-clone it. This is such a handy feature that you might want to use a Bash function to always push and export your stashes at the same time. Example:
git stash push --include-untracked --message "A demonstration";
git stash export --to-ref "refs/stashes/$USER";
git push --no-verify --force origin "refs/stashes/$USER"
What the above is doing is:
-
Pushing to your local stash (as discussed earlier).
-
Pushing your local stash to your remote server (as discussed earlier).
-
Performing a force push with Git Hooks disabled.
At the moment, Git doesn’t have a nice way to use --force-with-lease which would be ideal but I’ve logged this issue with the core team and hope this will be fixed in the future. Unfortunately, for now, you need to use a force push in order to ensure your stash is properly updated.
As for the --no-verify, this is because you most likely don’t want all of your standard code quality checks to be performed when pushing a stash. For standard commit creation and pushing, yes, but not for your stashes.
Import
Prior to Git 2.51.0, you couldn’t import a stash but that has changed. Once your stash has been exported you can import it as follows:
git stash import "refs/stashes/$USER"
The above is a quick way to restore your entire stash should you have cleared your local stash, re-cloned your repository, etc.
Workflow
When you put all of the above together, you end up with a nice workflow that might look something like this:
# Spike some work and stash it for later.
touch one.txt
git stash push --include-untracked --message "One"
touch two.txt
git stash push --include-untracked --message "Two"
touch three.txt
git stash push --include-untracked --message "Three"
# Export and push local changes to the remote.
git stash export --to-ref "refs/stashes/$USER"
git push --no-verify --force origin "refs/stashes/$USER"
# List all stashes.
git stash list --pretty=format:'%C(magenta)%gd%C(reset) %C(yellow)%h%C(reset) %s %C(green)(%cr)%C(reset)'
# stash@{0} 99227c28421b On main: Three (10 seconds ago)
# stash@{1} ec1cccb8ea3f On main: Two (10 seconds ago)
# stash@{2} e36e9a855344 On main: One (10 seconds ago)
# Pop the first stash to modify it.
git stash pop stash@{2}
printf "%s\n" "Curabitur eleifend wisi iaculis ipsum." > one.txt
# Push the modfication.
git stash push --include-untracked --message "One (modified)"
# List stashes once again to verify.
git stash list --pretty=format:'%C(magenta)%gd%C(reset) %C(yellow)%h%C(reset) %s %C(green)(%cr)%C(reset)'
# stash@{0} 5129180097c1 On main: One (modified) (27 seconds ago)
# stash@{1} 99227c28421b On main: Three (71 seconds ago)
# stash@{2} ec1cccb8ea3f On main: Two (71 seconds ago)
# Delete the second stash (and ensure the remote is updated too).
git stash drop stash@{2}
git push --no-verify --force origin "refs/stashes/$USER"
# Clear your local stash (to simulate loosing it).
git stash clear
# Restore your stash by importing it.
git stash import refs/stashes/$USER
# List your stash and then pop the "One" stash to verify the import is OK.
git stash list --pretty=format:'%C(magenta)%gd%C(reset) %C(yellow)%h%C(reset) %s %C(green)(%cr)%C(reset)'
git stash pop stash@{2}
cat one.txt
# Curabitur eleifend wisi iaculis ipsum.
You’ll definitely want to wrap this functionality in individual Bash functions that will automate the pushing and popping for an enhanced workflow. Keep an eye on this space as the Git core team works to improve the import/export of stashes as well.
Conclusion
You’ve learned how to manage multiple stashes while also being able to export and import. You’ve also learned how to automate this process and enhance the output through the use of pretty formatting. Even better, you can now share your stashes with others or manage your stashes across multiple computers which was something you couldn’t do before. Enjoy and may you context switch with greater ease!
