Git stash uncached: how to put away all unstaged changes?

GitGit Stash

Git Problem Overview


Suppose two set of changes are made in a project versioned by git. One set is staged and the other is not.

I would like to recheck staged changes by running my project at this state (before commit). What is a simple way to put away all unstaged changes and leave only staged? So I need unstaged changes to disappear from my project, but to be stored somewhere for further work.

This is sounds very much like git stash command. But git stash would put both unstaged and staged changes away from my project. And I can't find something like git stash uncached.

Git Solutions


Solution 1 - Git

Update 2:
I'm not sure why people are complaining about this answer, it seems to be working perfectly with me, for the untracted files you can add the -u flag

The full command becomes git stash --keep-index -u

And here's a snippet from the git-stash help

> If the --keep-index option is used, all changes already added to the > index are left intact. > > If the --include-untracked option is used, all untracked files are > also stashed and then cleaned up with git clean, leaving the working > directory in a very clean state. If the --all option is used instead > then the ignored files are stashed and cleaned in addition to the > untracked files.

And this is a gif of how it looks:

enter image description here

Update:
Even though this is the selected answer, a lot have pointed out that the answer below is the correct one, I recommend checking it out.

I tested my answer again today (31/1/2020) against git version 2.24.0, and I still believe that it's correct, I added a small note above about the untracked files. If you think it's not working please also mention your git version.

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

git stash --keep-index

From the documentation of git-stash:

> Testing partial commits > ----------------------- > > You can use git stash save --keep-index when you want to make two or > more commits out of the changes in the work tree, and you want to test > each change before committing: > > # ... hack hack hack ... > $ git add --patch foo # add just first part to the index > $ git stash save --keep-index # save all other changes to the stash > $ edit/build/test first part > $ git commit -m 'First part' # commit fully tested change > $ git stash pop # prepare to work on all other changes > # ... repeat above five steps until one commit remains ... > $ edit/build/test remaining parts > $ git commit foo -m 'Remaining parts'

But, if you just want to visually check the staged changes only, you can try difftool:

git difftool --cached

Solution 2 - Git

The accepted answer also stashes staged changes as a few have pointed out. Here's a way to do it without getting your staged changes in the stash.

The idea is to do a temporary commit of your staged changes, then stash the unstaged changes, then un-commit the temp commit:

# temp commit of your staged changes:
$ git commit --message "WIP"

# -u option so you also stash untracked files
$ git stash -u

# now un-commit your WIP commit:
$ git reset --soft HEAD^

At this point, you'll have a stash of your unstaged changes and will only have your staged changes present in your working copy.

Solution 3 - Git

I found the marked answer did not work for me since I needed something which truly stashed only my unstaged changes. The marked answer, git stash --keep-index, stashes both the staged and unstaged changes. The --keep-index part merely leaves the index intact on the working copy as well. That works for OP, but only because he asked a slightly different question than he actually wanted the answer for.

The only true way I've found to stash unstaged changes is to not use the stash at all:

git diff >unstaged.diff
git apply -R unstaged.diff

You can also use git restore . or git checkout -- . instead of git apply -R unstaged.diff.

Work work work...

git apply unstaged.diff
rm unstaged.diff

Solution 4 - Git

Since the various answers here so far seem to have their own intricacies/limitations, I wanted to provide a few more alternatives that cover all of the specific edge cases that I personally have needed.

tl;dr

List staged (without deleted) files:

git diff --staged --diff-filter=d --name-only

List unstaged (without deleted) files:

git diff --diff-filter=d --name-only

List unstaged/untracked files:

git ls-files --modified --others --exclude-standard

Stashing only staged files (originally from this StackOverflow answer, but tweaked slightly):

git stash push --include-untracked -- $(git diff --staged --diff-filter=d --name-only)

Stashing only unstaged (not untracked) files:

git stash push --keep-index -- $(git diff --diff-filter=d --name-only)

Stashing unstaged and untracked files:

git stash push --keep-index --include-untracked -- $(git ls-files --modified --others --exclude-standard)

Stashing staged/unstaged files, while also keeping the staged files in your index as well:

git stash push --keep-index

Stashing staged/unstaged/untracked files, while also keeping the staged files in your index as well:

git stash push --include-untracked --keep-index

Full Explanation

git stash push allows us to provide a <pathspec>, and only stash the files that match it:

git stash push -- <pathspec>

> push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>…​] > > Save your local modifications to a new stash entry and roll them back to HEAD (in the working tree and in the index). The part is optional and gives the description along with the stashed state.

> <pathspec>…​ > > This option is only valid for push command. > > The new stash entry records the modified states only for the files that match the pathspec. The index entries and working tree files are then rolled back to the state in HEAD only for these files, too, leaving files that do not match the pathspec intact.

> -u, --include-untracked, --no-include-untracked > > When used with the push and save commands, all untracked files are also stashed and then cleaned up with git clean.


git diff allows us to list the currently unstaged files with --name-only:

git diff --name-only

> git diff [<options>] [--] [<path>…​] > > This form is to view the changes you made relative to the index (staging area for the next commit).

> --name-only > > Show only names of changed files.

> --diff-filter=\[(A|C|D|M|R|T|U|X|B)…​\[*\]\] > > Select only files that are Added (A), Copied (C), Deleted (D), Modified (M), Renamed (R), have their type (i.e. regular file, symlink, submodule, …​) changed (T), are Unmerged (U), are Unknown (X), or have had their pairing Broken (B). Any combination of the filter characters (including none) can be used. > > Also, these upper-case letters can be downcased to exclude. E.g. --diff-filter=ad excludes added and deleted paths.


git ls-files allows us to list both the --modified files AND the untracked (--others) files:

git ls-files --modified --others --exclude-standard

> git-ls-files - Show information about files in the index and the working tree

> -m, --modified > > Show modified files in the output

> -o, --others > > Show other (i.e. untracked) files in the output

> --exclude-standard > > Add the standard Git exclusions: .git/info/exclude, .gitignore in each directory, and the user’s global exclusion file.

Solution 5 - Git

Git: Stash unstaged changes

This will stash all modifications that you did not git add:

git stash -k

Note that newly created (and non-added) files will remain in your working directory unless you also use the -u switch.

git stash -k -u 

Also, your working directory must be clean (i.e. all changes need to be added) when you git stash pop later on.

http://makandracards.com/makandra/853-git-stash-unstaged-changes

Solution 6 - Git

Here is a way to make this very simple:

  1. Add the alias definitions below to your .zshrc or .bashrc or .bash_profile
  2. Now, anytime you are in this situation, just type gss and you will have 2 stashes - 1 with all the changes, another with only the staged changes

So, you can now apply the staged changes, see if those work, commit them if you want. Then, later you can also bring in the unstaged changes by applying the 'all WIP' stash and try those out.

alias gsts='git stash save'
alias gsv="git stash save --keep-index"

# How to Git Stash preserving staged/unstaged situation.
# 1. gsv will stash all and reset (remove) the unstaged, leaving only staged in staged state.
# 2. gsts will make a stash with your "good" (staged) files
alias gss='gsv all WIP && gsts staged WIP'

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionklm123View Question on Stackoverflow
Solution 1 - GitMohammad AbuShadyView Answer on Stackoverflow
Solution 2 - Gitstephen.hansonView Answer on Stackoverflow
Solution 3 - GitBinary PhileView Answer on Stackoverflow
Solution 4 - GitGlenn 'devalias' GrantView Answer on Stackoverflow
Solution 5 - GitCory DanielsonView Answer on Stackoverflow
Solution 6 - GitMichael LiquoriView Answer on Stackoverflow