I have been wanting to use a git command that saves a stash without modifying my working tree, as a lightweight backup that's safe from any git resets or whatever I might do to screw up my index. Basically the functional equivalent of "git stash save && git stash apply" except that the working copy is never touched, since this can make certain text editors/IDE's cranky.
Something like this is approaching what I want, but not quite:
git update-ref refs/stash `git stash create "Stash message"`
This works functionally, but the issue I'm having is that no stash message shows up in "git stash list" even though the actual stash commit does have my message in it. Considering how large a stash can get, stash messages are pretty important.
You need to pass the message to
update-ref
, notstash create
asstash create
doesn't take a message (it doesn't update any ref, so it has no reflog entry to populate).git stash store "$(git stash create)"
Will create stash entry similar to what you would get with
git stash
without actually touching and clearing your working directory and index.If you check stash list or look at all commit graph (including stash) you'll see that it's similar result to what you would get with normal call to
git stash
. Just the message in stash list is different (normally it's something like "stash@{0}: WIP on master: 14e009e init commit", here we'll get "stash@{0}: Created via "git stash store"")A bit more explanation:
A git stash entry is represented using normal commits with some defined structure. Basically it is a regular commit object that has 2 parents (or 3 if you use
--include-untracked
option) (more info 1,2).git stash create
creates this commits that represents stash entry and returns you the object name (SHA-1) of commit object (the one that has 2 or 3 parents). It is a dangling commit (you can verify it by callinggit fsck
aftergit stash create
). You need to makerefs/stash
point to this dangling commit and you do it bygit stash store
(or bygit update-ref
like in other answers, becausegit stash store
usesgit update-ref
to do its work).It's good to look at actual source code of
git stash push
and see that it's basically callinggit stash create
andgit stash store
and then does some logic to clean files (which one depends on what options you used ingit stash push
).Thanks to Charles' tip, I whipped up a bash script to do exactly what I wanted (I was running into issues implementing this as only an alias). It takes an optional stash message just like git stash save. If none is supplied it will use the default message generated by git stash.
Edit: As pointed out in a comment below, saving this script as
git-stash-snap
somewhere in your path is sufficient to be able to invoke it by typinggit stash-snap
.The nice thing here is that even if you drop a stash made with this method, you will still be able to see the stash message using git log [commit-hash] of the dangling commit!
Edit: since git 2.6.0 you can add
--create-reflog
toupdate-ref
and thengit stash list
will show this even ifgit stash
was not used before.Edit: Git has introduced a new stash subcommand called
stash push
so I have updated my recommendation for naming this script fromgit-stash-push
togit-stash-snap
.Inspired by Eliot's solution, I extended his script a little bit:
I implemented the following changes to Eliot's script:
-c
is used, if there no changes compared to the last stash, the script will exit. This is useful if you use this script as a "time machine", making an automated stash every 10 minutes. If nothing has changed, no new stash is created. Without this switch, you might end up with n consecutive stashes which are the same.Not that in order for the switch
-c
to work properly, at least one stash must exist, otherwise the script throws an error ongit diff stash@{0}
and will do nothing.I use this script as a "time machine", snapshotting every 10 minutes using the following bash loop: