318

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.

smci
  • 32,567
  • 20
  • 113
  • 146
Pat Notz
  • 208,672
  • 30
  • 90
  • 92
  • Are there changes in the working tree other than the applied stash? – Greg Bacon Jun 19 '09 at 22:24
  • 1
    Can't just just create a reversed patch by diffing between the current and previous revision? And then apply that one? – rtn Jun 19 '09 at 21:49
  • Adding this here, ... was supposed to be a FAQ not a question... https://stackoverflow.com/questions/59973103/git-how-to-undo-revert-a-saved-stash-by-message-easily/59973105#59973105 – Don Thomas Boyle Jan 29 '20 at 18:10

13 Answers13

235

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...

Kemal Fadillah
  • 9,760
  • 3
  • 45
  • 63
Greg Bacon
  • 134,834
  • 32
  • 188
  • 245
  • 2
    Awesome, thanks. Seems like this might be a nice feature for stash. – Pat Notz Jun 21 '09 at 14:28
  • 7
    Yes, `git apply -R` is an improvement, at least for me on my windows box with git bash as `patch --reverse` had problems to locate the file to patch (no real clue why the alternative worked). +1 and good explanation – hakre Aug 22 '13 at 10:57
  • wouldn't it be better to add `--index` just like this `git stash show -p | git apply --reverse --index`. Because you no longer need to add in the index the changes that are reverted back. – theUnknown777 Apr 13 '15 at 11:15
  • 3
    @Greg Bacon, hey, I've tried to go through the script that you outlined, but the patch failed when I ran `git stash show -p | git apply -R -v` with the message: `Checking patch messages... error: while searching for: Hello, world Hello again error: patch failed: messages:1`. Do you know what could be wrong? – Max Koretskyi Apr 25 '15 at 05:10
  • 8
    I get **error: patch failed: /src/filename.java:46** **error: src/filename.java patch does not apply** – Tim Boland Aug 15 '17 at 01:08
  • That's beautifully useful ; I'm working with the Terraform sources, which soil their own code by inserting autogenerated code alongside user code when you build them. With the autogenerated code in a stash, I can unpick that instantly like this. – Adrian Apr 05 '19 at 10:05
  • 1
    The `…| git apply --reverse` works for me. The `…| patch --reverse` fails with `can't find file to patch at input line 5`. Archlinux system here, patch version 2.7.6. – Hi-Angel Jun 14 '19 at 06:49
  • Love the reverse one-liner. Many thanks! – CaTx Mar 12 '23 at 15:46
216
git checkout -f

will remove any non-commit changes.

salman
  • 2,209
  • 1
  • 8
  • 3
  • 7
    thanks, you help me from staged change which was not unapply. – Fa.Shapouri Jan 15 '17 at 13:53
  • 12
    This was much simpler – Mark A Jul 09 '18 at 20:46
  • This caused some odd problems for me. My unit tests failed with obscure messages saying it couldn't read ARM. I had to clean my build folder, derived data, and then restart my machine to get back to normal. – ScottyBlades Sep 01 '20 at 22:42
  • 17
    The OP did not ask for this though as this will remove all non-committed changes, not only changes introduced by the "stash apply". This can lead to loss of useful changes. – Kostas Nov 05 '20 at 14:38
  • This is what I wanted, was able to run this, switch to my original branch and run stash apply to get my changes back. – Max Conradt Dec 28 '22 at 22:46
95

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.

Kemal Fadillah
  • 9,760
  • 3
  • 45
  • 63
Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
40

The V1 git man page had a reference about un-applying a stash. The excerpt is below.

The newer V2 git man page doesn't include any reference to un-applying a stash but the below still works well

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 un-apply 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
Choco Smith
  • 1,658
  • 18
  • 24
  • 2
    For whatever reason this helpful section you linked "Un-applying a Stash" was removed from the 2nd version _-Latest version right now is 2.1.146, 2019-04-15-_ of this book [V2- Git Tools - Stashing and Cleaning](https://git-scm.com/book/en/v2/Git-Tools-Stashing-and-Cleaning). Could be because the authors think there's a better way to do this that I can't seem to find. – NadAlaba Apr 23 '19 at 11:30
  • 1
    @NadAlaba thanks for the heads up, have updated the answer to make note on the difference between v1 and v2... weird the git authors removed the section about un-applying a stash – Choco Smith Dec 04 '19 at 21:58
  • 1
    This answer is especially useful for showing how to specify the applied stash. Thanks for including that important info! for those who might also have included untracked files, you can use `git stash show -p --all stash@{3} | git apply -R` – Kay V Sep 07 '20 at 19:03
15

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!

Mark A
  • 1,995
  • 4
  • 18
  • 26
Slim Sim
  • 171
  • 1
  • 4
  • Just to be sure, I applied a `git stash apply --reverse` first and then simply went back to `git stash apply stash@{x}` like you mention. Worked with no problems. – Carlos Garcia May 20 '14 at 14:10
13

How to reverse apply a stash?

Apart from what others have mentioned, easiest way is first do

git reset HEAD

and then checkout all local changes

git checkout . 
Achal
  • 11,821
  • 2
  • 15
  • 37
  • 3
    This is by far the easiest way as long as you have *absolutely no* local work you want to save. If you applied the wrong stash to a branch or hit merge conflicts that you don't want to resolve, this is the quick and easy way to undo it by completely reverting your working set to your branch's latest commit. – Shadoninja Apr 06 '20 at 20:26
6

you can apply two commands

git reset . // to reverse the files

then

git checkout . // to reverse the changes

  • 6
    Please improve the quality of the answer by adding an explanations to each one of the commands. Thanks. – Slavik N Jul 17 '21 at 15:10
3

You can follow the image i shared to unstash if u accidentally tapped stashing.

Mickael B.
  • 4,755
  • 4
  • 24
  • 48
Clean Coder
  • 496
  • 5
  • 12
2

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
MHosafy
  • 176
  • 3
  • 12
2
git stash show -p | git apply --reverse

Warning, that would not in every case: "git apply -R"(man) did not handle patches that touch the same path twice correctly, which has been corrected with Git 2.30 (Q1 2021).

This is most relevant in a patch that changes a path from a regular file to a symbolic link (and vice versa).

See commit b0f266d (20 Oct 2020) by Jonathan Tan (jhowtan).
(Merged by Junio C Hamano -- gitster -- in commit c23cd78, 02 Nov 2020)

apply: when -R, also reverse list of sections

Helped-by: Junio C Hamano
Signed-off-by: Jonathan Tan

A patch changing a symlink into a file is written with 2 sections (in the code, represented as "struct patch"): firstly, the deletion of the symlink, and secondly, the creation of the file.

When applying that patch with -R, the sections are reversed, so we get: (1) creation of a symlink, then (2) deletion of a file.

This causes an issue when the "deletion of a file" section is checked, because Git observes that the so-called file is not a file but a symlink, resulting in a "wrong type" error message.

What we want is: (1) deletion of a file, then (2) creation of a symlink.

In the code, this is reflected in the behavior of previous_patch() when invoked from check_preimage() when the deletion is checked.
Creation then deletion means that when the deletion is checked, previous_patch() returns the creation section, triggering a mode conflict resulting in the "wrong type" error message.

But deletion then creation means that when the deletion is checked, previous_patch() returns NULL, so the deletion mode is checked against lstat, which is what we want.

There are also other ways a patch can contain 2 sections referencing the same file, for example, in 7a07841c0b ("git-apply: handle a patch that touches the same path more than once better", 2008-06-27, Git v1.6.0-rc0 -- merge). "git apply -R"(man) fails in the same way, and this commit makes this case succeed.

Therefore, when building the list of sections, build them in reverse order (by adding to the front of the list instead of the back) when -R is passed.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
0

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
}
  1. Create stash with name (message) $ git stash save "my stash"
  2. To appply named $ apply "my stash"
  3. To remove named stash $ remove "my stash"
lifesoordinary
  • 123
  • 2
  • 9
0

I had a similar issue myself, I think all you need to do is git reset --hard and you will not lose your changes or any untracked changes.

If you read the docs in git stash --help it states that apply is "Like pop, but do not remove the state from the stash list" so the state still resides there, you can get it back.

Alternatively, if you have no conflicts, you can just git stash again after testing your changes.

If you do have conflicts, don't worry, git reset --hard won't lose them, as "Applying the state can fail with conflicts; in this case, it is not removed from the stash list. You need to resolve the conflicts by hand and call git stash drop manually afterwards."

Tom Dickman
  • 169
  • 2
  • 5
0

For me, i had just typed the wrong name of the repository when checking out. So there literally was not remote to pull.

So, in addition to checking the casing, check the branch name spelling, or better yet, copy and paste it -to rule it out.

SeanMC
  • 1,960
  • 1
  • 22
  • 33