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.
Join the DZone community and get the full member experience.
Join For FreeWelcome 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.
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.
Autostash
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.
</retro>
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.
Opinions expressed by DZone contributors are their own.
Comments