265

Is there a git stash command that stashes your changes, but keeps them in the working directory too? So basically a git stash; git stash apply in one step?

SQB
  • 3,926
  • 2
  • 28
  • 49
Michael Dorst
  • 8,210
  • 11
  • 44
  • 71
  • 1
    Same question: https://stackoverflow.com/q/6315459/350384 – Mariusz Pawelski Mar 27 '20 at 17:10
  • Does this answer your question? [Git command to save a stash without modifying working tree?](https://stackoverflow.com/questions/6315459/git-command-to-save-a-stash-without-modifying-working-tree) – Mariusz Pawelski Mar 27 '20 at 17:28
  • 2
    @MariuszPawelski No, not really. That question is more specific than mine. The answer to my question was simply "no". Thanks for the link though, it may be helpful to some people, or even myself at a later time. – Michael Dorst Mar 29 '20 at 06:58
  • 1
    To be clear, my question is different because I have no requirement that the files remain untouched. I was merely looking for alternatives to `git stash && git stash apply`. You'll notice that the answers to that question are quite different from mine. – Michael Dorst Mar 29 '20 at 07:04
  • ah, right, you question is a bit less specific. But I put that question because its answers also fulfill your requirement. And that way this question appear as "Linked" in sidebar, so it might be helpful to someone. – Mariusz Pawelski Mar 29 '20 at 11:10

5 Answers5

256

For what it's worth, another way to do this is to stage the changes you want to keep, and then stash everything using --keep-index:

$ git add modified-file.txt
$ git stash push --keep-index

The commands above will stash everything, but it will leave the files staged in your working directory.

From the official Linux Kernel Git documentation for git stash or from git-scm:

If the --keep-index option is used, all changes already added to the index are left intact.

Cirelli94
  • 1,714
  • 1
  • 15
  • 24
  • 13
    this is by far the most straightforward explanation of --keep-index I've seen. I didn't get quite the meaning by the way it was worded on the docs. – 40detectives Mar 17 '20 at 18:33
  • 1
    if you want all staged before the stash, then just do `git add --all` – mfaani Sep 20 '22 at 18:57
  • 2
    I did `git stash push --keep-index -m "message"` and it still cleared the working directory! And I cleared the working directory changes again because I thought I had changed and saved old files, and applied the stash, and I find out not all files I had are in the stash, and are now gone! – Mark Jeronimus Aug 07 '23 at 12:14
  • @MarkJeronimus could be because the files were not staged using `git add` – VishnuVS Aug 18 '23 at 07:39
91

git stash and then git stash apply (git stash && git stash apply) will stash files and apply stash immediately after it. So after all you will have your changes in stash and in working dir.

You can create an alias if you want it in one piece. Just put something like this to ~/.gitconfig:

[alias]
    sta = "!git stash && git stash apply"

The drawback of this approach is that all files are stashed and recreated. This means that timestamps on the files in question will be changed. (Causing Emacs to complain when I try to save the file if opened it before I did the git sta, and may cause unnecessary rebuilds if you're using make or friends.)

zrajm
  • 1,361
  • 1
  • 12
  • 21
madhead
  • 31,729
  • 16
  • 153
  • 201
  • 3
    also, what is the difference between `git stash; git stash apply` and `git stash && git stash apply`? – Michael Dorst Jul 24 '13 at 20:15
  • 17
    I believe the difference is that [`&&` runs second command only if first returned zero status code](http://linux-training.be/files/books/html/fun/ch11s04.html). – madhead Jul 24 '13 at 20:18
  • 3
    @anthropomorphic `git config --global alias.sta "!git stash && git stash apply"` should do it. –  Jul 24 '13 at 20:37
  • How can I modify this alias to use `git stash save` with an argument and _then_ do `git stash apply`? – spinningarrow Oct 09 '14 at 14:40
  • @spinningarrow try "!git stash $1 && git stash apply" – madhead Oct 09 '14 at 20:52
  • Why is there a bang (!) before the 'git' command? Bash tells me "-bash: !git: command not found. – Jay Sidri May 05 '17 at 01:48
  • 1
    @JaySidri, bang means that it is actually external command, not a git argument itself. [As per docs](https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases): "As you can tell, Git simply replaces the new command with whatever you alias it for. However, maybe you want to run an external command, rather than a Git subcommand. In that case, you start the command with a ! character." – madhead May 10 '17 at 12:20
  • not a huge fan of this since if you've already dealt with a conflict this will force you to deal with it again. – gabeio May 02 '18 at 17:08
  • As far as my requirement, this is the most straightforward method since `--keep-index` required staging changes. – Shriraj Hegde Oct 31 '21 at 10:36
26

You can use git stash create to create a stash commit, and then save it to the stash using git stash store:

git stash store $(git stash create) -m "Stash commit message"

This can be saved to a git alias to make it more convenient:

git config --global alias.stash-keep '!git stash store $(git stash create)'

git stash-keep -m "Stash commit message"

Note that this does not do everything that git stash push does. For instance, it does not append the branch name to the commit, e.g. "stash@{0}: On myBranch: Stash commit message".

M. Justin
  • 14,487
  • 7
  • 91
  • 130
  • 1
    Love this one!! One correction though: `man git-stash` says the `-m ` has to come before the commit hash. Except something change in newest git. – tanius Jun 13 '20 at 17:33
  • 3
    This answer actually does exactly what the OP was asking! – jasongregori Jan 12 '21 at 17:35
  • I had trouble getting this alias to work, I eventually went with `save = "!f() { git stash store $(git stash create); }; f"` which worked. – jasongregori Jan 12 '21 at 17:54
  • 1
    This should be the accepted answer, as the other solutions on top are unclean - mess up the (possibly expensive) index state and/or timestamps. Note: you can apply changes from only index to worktree later with `git cherry-pick -m2 stash` – kxr Sep 20 '22 at 15:37
  • Git message goes (without `-m`) on the `create`, not on the `store`: `git stash store $(git stash create "Stash commit message")` (using git version 2.39.1) – Jannes May 14 '23 at 11:03
  • @Jannes I just tested this on 2.39.2, and specifying the message on the `store` (`git stash store d825ce7 -m "store messge"`) sets the stash message (as seen in `git stash list`). Setting it on the create (`git stash create "create message"`) sets the message of the dangling merge commit, but that is not used as the stash message when subsequently stored. So what I have in the answer appears to be right. Both are documented options of [`git stash`](https://git-scm.com/docs/git-stash), but the `store -m ` one does what I'm looking for. – M. Justin May 15 '23 at 01:38
  • 1
    Yeah, it turned out to be a side effect of the git GUI client I was using, which showed the the other message. In the end I had to set _both_ in order to get `git stash list` to also show it correctly. – Jannes May 15 '23 at 11:30
15

A small enhancement in the answer which in practical may likely to use.

$ git add modified-file.txt  
(OR $ git add .    ---- for all modified file)
$ git stash save --keep-index "Your Comment"
Premchandra Singh
  • 14,156
  • 4
  • 31
  • 37
3

There's a trick may help you, not stash' thing but FWIW:

git commit -m "Your already staged content (May be empty)"   #(Commit index)
git add -A                    #(stage remaining 'unstaged' contents)
git commit -m "My works so far (Unstaged)"       #(Working directory)
git tag stash                 #(mark the commit with 'stash' tag)
git reset HEAD^                   #(1st reset (--mixed): Unstaged)
git reset --soft HEAD^        #(2nd reset (--soft): Staged (Skip if your index was empty))

And so now you have a commit tagged stash at your disposal, it's not possible to do a git stash pop anyway but you can do things like creating patch or resetting files etc from the created 'stash' tag, your working dir files are also left intact BTW.


If you prefer to store in a branch:

current_branch=`git branch --show-current`
git checkout -b stash         #(create and switch to new branch 'stash')
git commit -m "Your already staged content (May be empty)"   #(Commit index)
git add -A                    #(stage remaining 'unstaged' contents)
git commit -m "My works so far (Unstaged)"         #(Working directory)
git reset HEAD^                   #(1st reset (--mixed): Unstaged)
git reset --soft $current_branch               #(2nd reset (--soft): Staged)
git checkout $current_branch        #(Now go back to where you've left with your working dir and staged status intact)
KenIchi
  • 1,129
  • 10
  • 22
  • 1
    "working dir intact" No, you would lose any staged changes, they would become unstaged after this. – mattalxndr Jun 02 '21 at 07:16
  • Thank you, while "working dir intact" holds true, if you would like to keep staging status also use the `--soft` option to the `reset` command. I've updated my answer. – KenIchi Sep 15 '22 at 07:01
  • (updated 202306): Keep staged changes, Added store in a branch option --> I prefer this since creating a new branch will not tamper with your main branch's reflog. – KenIchi Jun 22 '23 at 02:50