How to reverse apply a stash?
Question
I have a small patch saved away in my git stash. I've applied it to my working copy using git stash apply
. Now, I'd like to back out those changes by reverse applying the patch (kind of like what git revert
would do but against the stash).
Does anyone know how to do this?
Clarification: There are other changes in my working copy. My particular case is hard to describe but you can imagine some debugging or experimental code that's in the stash. Now it's mixed in my working copy with some other changes and I'd like to see the effect with and without the changes from the stash.
It doesn't look like stash supports this currently, but a git stash apply --reverse
would be a nice feature.
Solution
According to the git-stash manpage, "A stash is represented as a commit whose tree records the state of the working directory, and its first parent is the commit at HEAD
when the stash was created," and git stash show -p
gives us "the changes recorded in the stash as a diff between the stashed state and its original parent.
To keep your other changes intact, use git stash show -p | patch --reverse
as in the following:
$ git init
Initialized empty Git repository in /tmp/repo/.git/
$ echo Hello, world >messages
$ git add messages
$ git commit -am 'Initial commit'
[master (root-commit)]: created 1ff2478: "Initial commit"
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 messages
$ echo Hello again >>messages
$ git stash
$ git status
# On branch master
nothing to commit (working directory clean)
$ git stash apply
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: messages
#
no changes added to commit (use "git add" and/or "git commit -a")
$ echo Howdy all >>messages
$ git diff
diff --git a/messages b/messages
index a5c1966..eade523 100644
--- a/messages
+++ b/messages
@@ -1 +1,3 @@
Hello, world
+Hello again
+Howdy all
$ git stash show -p | patch --reverse
patching file messages
Hunk #1 succeeded at 1 with fuzz 1.
$ git diff
diff --git a/messages b/messages
index a5c1966..364fc91 100644
--- a/messages
+++ b/messages
@@ -1 +1,2 @@
Hello, world
+Howdy all
Edit:
A light improvement to this is to use git apply
in place of patch:
git stash show -p | git apply --reverse
Alternatively, you can also use git apply -R
as a shorthand to git apply --reverse
.
I've been finding this really handy lately...
OTHER TIPS
git stash
[save]
takes your working directory state, and your index state, and stashes them away, setting index and working area to HEAD
version.
git stash apply
brings back those changes, so git reset --hard
would remove them again.
git stash pop
brings back those changes and removes top stashed change, so git stash [save]
would return to previous (pre-pop) state in this case.
git checkout -f
will remove any non-commit changes.
Direct cut n paste from the git man page It's clearly worded and even includes an alias;
Un-applying a Stash In some use case scenarios you might want to apply stashed changes, do some work, but then un-apply those changes that originally came from the stash. Git does not provide such a stash unapply command, but it is possible to achieve the effect by simply retrieving the patch associated with a stash and applying it in reverse:
$ git stash show -p stash@{0} | git apply -R
Again, if you don’t specify a stash, Git assumes the most recent stash:
$ git stash show -p | git apply -R
You may want to create an alias and effectively add a stash-unapply command to your Git. For example:
$ git config --global alias.stash-unapply '!git stash show -p | git apply -R'
$ git stash apply
$ #... work work work
$ git stash-unapply
This is long over due, but if i interpret the problem correctly i have found a simple solution, note, this is an explanation in my own terminology:
git stash [save]
will save away current changes and set your current branch to the "clean state"
git stash list
gives something like: stash@{0}: On develop: saved testing-stuff
git apply stash@{0}
will set current branch as before stash [save]
git checkout .
Will set current branch as after stash [save]
The code that is saved in the stash is not lost, it can be found by git apply stash@{0}
again.
Anywhay, this worked for me!
In addition to @Greg Bacon answer, in case binary files were added to the index and were part of the stash using
git stash show -p | git apply --reverse
may result in
error: cannot apply binary patch to '<YOUR_NEW_FILE>' without full index line
error: <YOUR_NEW_FILE>: patch does not apply
Adding --binary
resolves the issue, but unfortunately haven't figured out why yet.
git stash show -p --binary | git apply --reverse
This is in addition to the above answers but adds search for the git stash based on the message as the stash number can change when new stashes are saved. I have written a couple of bash functions:
apply(){
if [ "$1" ]; then
git stash apply `git stash list | grep -oPm1 "(.*)(?=:.*:.*$1.*)"`
fi
}
remove(){
if [ "$1" ]; then
git stash show -p `git stash list | grep -oPm1 "(.*)(?=:.*:.*$1.*)"` | git apply -R
git status
fi
}
- Create stash with name (message)
$ git stash save "my stash"
- To appply named
$ apply "my stash"
- To remove named stash
$ remove "my stash"