The 2016 Git Retrospective: Stash

DZone 's Guide to

The 2016 Git Retrospective: Stash

If you like Git rebase, you might be familiar with autostash. It automatically stashes local changes to your working copy before rebasing and applies them after rebasing.

· Agile Zone ·
Free Resource

Welcome to the sixth (and final) entry in our Git in 2016 retrospective! In part five, we covered some great changes one of Git's more contentious features: submodules. In this final post, we're going to look at enhancements made to a unique feature of Git that is almost universally loved: the git stash command. 

Image title

January's Git v2.7 release enhanced git stash output with the stash.showPatch property and rounded out the rebase.autostash config property with the --no-autostash option. Then, in November, Git v2.11 made accessing your stash much smoother with simplified git stash identifiers.


If you're a fan of git rebase, you might be familiar with the --autostash option. It automatically stashes any local changes made to your working copy before rebasing and reapplies them after the rebase is completed.

$ git rebase master --autostash
Created autostash: 54f212a
HEAD is now at 8303dca It's a kludge, but put the tuple from the database in the cache.
First, rewinding head to replay your work on top of it...
Applied autostash.

This is handy, as it allows you to rebase from a dirty worktree. There's also a handy config flag named rebase.autostash to make this behavior the default, which you can enable globally with:

$ git config --global rebase.autostash true

rebase.autostash has actually been available since Git 1.8.4, but v2.7 introduces the ability to cancel this flag with the --no-autostash option. If you use this option with unstaged changes, the rebase will abort with a dirty worktree warning:

$ git rebase master --no-autostash
Cannot rebase: You have unstaged changes.
Please commit or stash them.

Stashes as Patches

Speaking of config flags, Git v2.7 also introduces stash.showPatch. The default behavior of git stash show is to display a summary of your stashed files.

$ git stash show
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

Passing the -p flag puts git stash show into "patch mode," which displays the full diff:

$ git stash show -p

diff --git a/package.json b/package.json
index c876b26..e21eeb3 100644
--- a/package.json
+++ b/package.json
@@ -48,7 +48,7 @@
     "mkdirp": "^0.5.0",
     "byline": "^4.2.1",
     "express": "~3.3.4",
-    "git-guilt": "^0.1.0",
+    "git-guilt": "^0.1.1",
     "jsonfile": "^2.0.0",
     "jugglingdb-sqlite3": "0.0.5",
     "jugglingdb-postgres": "~0.1.0",

stash.showPatch makes this behavior the default. You can enable it globally with:

$ git config --global stash.showPatch true

If you enable stash.showPatch but then decide you want to view just the file summary, you can get the old behavior back by passing the --stat option instead.

$ git stash show --stat
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

As an aside: --no-patch is a valid option but it doesn't negate stash.showPatch as you'd expect. Instead, it gets passed along to the underlying git diff command used to generate the patch, and you'll end up with no output at all!

Simple Stash IDs

If you're a  git stash fan, you probably know that you can shelve multiple sets of changes, and then view them with git stash list:

$ git stash list

stash@{0}: On master: crazy idea that might work one day
stash@{1}: On master: desperate samurai refactor that should probably never be merged
stash@{2}: On master: viable performance improvement that I forgot I stashed
stash@{3}: On master: pop this when ready to use Docker in production

However, you may not know why Git’s stashes have such awkward identifiers (stash@{1}stash@{2}, etc.) and may have written them off as "just one of those Git idiosyncrasies." It turns out that like many Git features, these weird IDs are actually a symptom of a very clever use (or abuse) of the Git data model.

Under the hood, the git stash command actually creates a set of special commit objects that encode your stashed changes and maintains a reflog that holds references to these special commits. This is why the output from git stash list looks a lot like the output from the git reflog command. When you run git stash apply stash@{1}, you're actually saying, “Apply the commit at position 1 from the stash reflog.”

As of Git 2.11, you no longer have to use the full stash@{n} syntax. Instead, you can reference stashes with a simple integer indicating their position in the stash reflog:

$ git stash show 1
$ git stash apply 1
$ git stash pop 1

And so forth. If you’d like to learn more about how stashes are stored, I wrote a little bit about it in this tutorial.


And we're done. Thanks for reading! I hope you enjoyed reading this series as much as I enjoyed spelunking through Git's source code, release notes, and man pages to write it. If you think I missed anything big, please let me know in the comments below or on Twitter and I'll endeavor to write a follow-up piece. If you stumbled on these articles out of order, you can check out the other topics covered in our Git in 2016 retrospective below:

Or, if you've read 'em all and still want more, check out Atlassian's Git tutorials (I'm a regular contributor there) for some tips and tricks to improve your workflow.

agile, git, stash, version control

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}