How to stash only unstaged changes in Git?

74,341

Solution 1

From Git 2.35+ (Q1 2022) you can now use the --staged flag (man) on git stash push to only stage the changes in your index.

Since your question asks the exact opposite, we have 2 choices:

  1. Reverse the operation like so:
git stash push --staged            # Stash staged changes
git stash                          # Stash everything else
git stash pop stash@{1}            # Restore staged changes stash
  1. Stage the changes you want to stash instead of the ones you want to keep. Now you can just run:
git stash push --staged

I got this information from this answer on another S/O post.

Solution 2

git stash push has an option --keep-index that does exactly what you need.

So, run git stash push --keep-index.

Solution 3

This may be done in 3 steps: save staged changes, stash everything else, restore index with staged changes. Which is basically:

git commit -m 'Save index'
git stash push -u -m 'Unstaged changes and untracked files'
git reset --soft HEAD^

This will do exactly what you want.

Solution 4

git stash save --keep-index

Also, Re:

Why not commit your changes after staging them? – Shin

A: Because you should always checkin tested code :) That means, you need to run the tests with only the changes you are about to commit

All this apart from the fact that of course, as an experienced programmer, you have the innate urge to test and review just those changes -- only partly kidding

Solution 5

Stash Without the Staged Changes

The Problem with --keep-index / -k

Stashing just the working tree (unstaged changes) in Git is more difficult than it should be. The accepted answer, and quite a few other answers, stashes the unstaged changes and leaves the stage alone as requested via --keep-index.

However what isn't obvious is that --keep-index also stashes the staged changes. The staged changes end up in both the stage AND the stash. This is rarely what one wants because any interim changes to the stash are likely to result in conflicts when popping the stash later.

Alias Solution

This alias works well to stage just the working copy changes:

stash-working = "!f() { \
  git commit --quiet --no-verify -m \"temp for stash-working\" && \
  git stash push \"$@\" && \
  git reset --quiet --soft HEAD~1; }; f"

It commits the staged changes temporarily, creates a stash from the remaining changes (and allows additional arguments such as --include-untracked and --message to be passed as alias arguments), and then resets the temporary commit to get back the staged changes.

It is similar to @Simon Knapp's answer, but with a few minor differences -- it uses --quiet on the temporary actions taken, and it accepts any number of parameters for the stash push, rather than hard-coding the -m, and it does add --soft to the final reset so that the index remains as it started. It also uses --no-verify on the commit to avoid changes to the working copy from pre-commit hooks (HT: @Granfalloner).

For the opposite problem of stashing just the staged changes (alias stash-index) see this answer.

Share:
74,341

Related videos on Youtube

Unapiedra
Author by

Unapiedra

Updated on February 23, 2022

Comments

  • Unapiedra
    Unapiedra over 1 year

    I would like to use this workflow:

    1. Stage some changes.
    2. Save the unstaged changes to the stash.
    3. Do some stuff with the things in stage (build, test, etc.).
    4. Commit.
    5. Restore the unstaged changes.

    Is there a way to do step 2?

    Example:

    git init
    echo one >file
    git add file
    git commit
    echo two >>file
    git add file
    echo three >>file
    git stash push
    test
    git commit
    git stash pop
    
    • Shizzmo
      Shizzmo over 11 years
      Why not commit your changes after staging them?
    • sehe
      sehe over 11 years
      IIRC --keepindex does exactly that
    • Unapiedra
      Unapiedra over 11 years
      Because if, say, the build fails I don't want to have a commit of this. I know I can delete the commit but I'd like to do this without a commit if possible.
    • Unapiedra
      Unapiedra over 11 years
      Sehe, thanks. I can confirm this works. Gee, I looked at the manual at linux.die.net/man/1/git-stash which is out of date. man git stash is much better.
    • jaf0
      jaf0 almost 9 years
      it's --keep-index, fwiw.
    • JonnyRaa
      JonnyRaa about 2 years
      this helped me learn that 'index' == staging area
  • vhallac
    vhallac over 11 years
    True. I keep using save with git stash. Maybe it is the programmer in me insisting on honoring the symmetry with apply/pop. :)
  • peterflynn
    peterflynn about 9 years
    Note: this still stashes all your changes; the only difference from regular git stash save is that it leaves the already-staged changes in your working copy as well. In the workflow above this would work fine since you're just applying the stash on top of a local copy that already has half of the stash's changes (which git is smart enough to ignore). But if you edit the code before re-applying the stash, you could potentially see merge conflicts when you go to apply. Fyi.
  • rjmunro
    rjmunro about 9 years
    @ytpete That has bitten me so many times. I really wish there was a way for git to only stash the things you are not keeping... I often commit stuff, then do a full git stash, knowing that I can git commit --ammend if there are problems in what I committed.
  • Rhubbarb
    Rhubbarb over 8 years
    --amend (rather than --ammend)
  • Rhubbarb
    Rhubbarb over 8 years
    I used the commit, stash save, amend-commit-if-necessary, stash pop method. This is fine so long as you don't amend after a push (i.e. only push after you're happy with the final amend). I'm going to try the --keep-index option on the basis that it's easy to opt to take 'upstream' or 'stashed' changes upon merge conflict. However, a properly considered resolution is probably best. As a slight aside, I find resolving conflicts to be very much easier when 3-way conflict annotations are enabled: set the "merge.conflictstyle" configuration variable to "diff3".
  • Rhubbarb
    Rhubbarb over 8 years
    I also prefer to explicitly specify save on a stash; that is helpful when you also want to provide a message string, which might otherwise be ambiguously interpreted by git. (Unlikely but possible.)
  • user643011
    user643011 about 6 years
    This solution does not work for me because of the problems described by peterflynn. It is not a good answer to the question since it still stashes the staged changes. Anybody got a better solution?
  • ma11hew28
    ma11hew28 over 4 years
    Note: -u also stashes untracked files.
  • jocull
    jocull over 3 years
    Docs seem to say that stash save is deprecated now: "This option is deprecated in favour of git stash push. It differs from "stash push" in that it cannot take pathspecs, and any non-option arguments form the message."
  • Alex
    Alex over 3 years
    It does not do what the question asked. It creates a stash that contains all modifications INCLUDING the index. What it does differently is that it does not 'resets' the index. But if you reset the index yourself and then 'stash pop', the index will be brought back. The question is about creating a stash that does NOT INCLUDE indexed files.
  • Inigo
    Inigo over 3 years
    This approach essentially duplicates what git stash save --keep-index does with a lot more work. I don't see any advantages.
  • Alexander Klauer
    Alexander Klauer about 3 years
    @vas No, the approach does not duplicate that. See peterflynn's comment to the accepted answer.
  • Dustin Oprea
    Dustin Oprea about 3 years
    This is awesome. It's a little labor-intensive, but at least you can skip and add whole files.
  • Steven Lu
    Steven Lu over 2 years
    I'm a bit frustrated. I began a long cherry-pick operation last night which involves some necessary conflict management. I didn't finish, and today I collaborated with my colleagues on an unrelated section of code (since I still had the build available and running, why not). Now i've got a bunch of unstaged changes I need to save, I don't need to commit in the course of continuing a range cherry-pick, so I want to avoid that, but I cannot stash because it would slurp up the unrelated changes i've staged. Seems like I will need to commit and hope for the best.
  • Steven Lu
    Steven Lu over 2 years
    Follow up, i was able to get through the situation by using git stash --patch
  • Granfalloner
    Granfalloner over 2 years
    As a further improvement to this snippet, its worth to add --no-verify option to git commit, otherwise implicit temporary commit might seriously mess up working directory because of pre-commit hook.
  • whenov
    whenov about 2 years
    @peterflynn You can use git diff in this scenario. Use git diff > ~/patch to export the changes between the staging area and working area, git checkout to discard the changes in the working area, do what you need to do with the staging area, then git apply ~/patch to restore the saved changes.
  • Jmartnz
    Jmartnz over 1 year
    This is exactly what I needed thanks
  • Maggyero
    Maggyero over 1 year
    @whenov The problem with your approach is that git checkout won’t discard anything in the working tree after git reset.
  • Maggyero
    Maggyero over 1 year
    Excellent, but the purpose of stashing unstaged changes was to test staged changes before committing them. So there is no point in executing your third command git reset --soft HEAD^.
  • citysurrounded
    citysurrounded over 1 year
    @user643011 see my answer here: stackoverflow.com/a/71150883/5469587
  • Georgiy Bukharov
    Georgiy Bukharov over 1 year
    Simple yet working! Thanks!
  • citysurrounded
    citysurrounded over 1 year
    No problem! Glad I could help @GeorgiyBukharov
  • VonC
    VonC over 1 year
    Good point. I have updated my ow answer on this page, in order to reference my other answer.
  • Anthony
    Anthony about 1 year
    Barf, sucks the feature doesn't exist
  • Anentropic
    Anentropic about 1 year
    I just ran this and it stashed my unstaged changes too
  • citysurrounded
    citysurrounded about 1 year
    @Anentropic that is exactly what OP asked for – to stash unstaged changes. Was that a typo?
  • Anentropic
    Anentropic about 1 year
    I think I mis-typed, it seemed to stash everything, staged too, but tbh maybe I confused that too
  • Adi H
    Adi H about 1 year
    Pro tip: You can generally combine single-character flags together: -u -m can become -um e.g. git stash push -um "Greatest stashing ever"
  • iRestMyCaseYourHonor
    iRestMyCaseYourHonor about 1 year
    I think this should be accepted answer because the one accepted does not work if you have some edits that are staged and unstaged on the same file, you get patch does not apply.
  • Lotus
    Lotus 12 months
    I had more luck using it as a function, not sure why but zsh choked on the above syntax: stash-working() { git commit --quiet --no-verify -m "temp for stash-working" && git stash push "$@" && git reset --quiet --soft HEAD~1; }
  • Raman
    Raman 12 months
    @Lotus Try copying it exactly as-is directly into the [alias] section of your .gitconfig. That way, zsh is not involved at all.
  • José Santos 11 months
    this will stash any file that has changes that are unstaged, which means it will also stash changes that were on the staged side of those files.