1

Does git stash push not stash unstaged changes?

$ touch testfile
$ git stash push
No local changes to save

Why does it work for staged changes now?

$ git add testfile 
$ git stash push
Saved working directory and index state WIP on mybranch: e5c1660  add note

Thanks.

1 Answers1

1

The git stash code saves, by default,1 exactly and only the tracked files. An untracked file is a file that is in the work-tree, but not in the index. Such a file is not going to be in the next commit you make, if you make a commit right now, whether or not the file is listed in a .gitignore.

Once the file is in the index, it will be in the next commit you make, whether or not it is listed in a .gitignore. So, once you git add testfile, there is something in the index that does not match something in HEAD and git stash is willing to make commits from both the index and work-tree. The git stash command (with no extra options) will save2 the index and work-tree if either one differs from HEAD, but will do nothing if both exactly match HEAD.


1I'm deliberately ignoring, here, the option to run git stash save -u or git stash save -a. These make a stash that includes three commits instead of two. It also changes the cleaning action that git stash save executes. A later git stash apply or git stash pop of a three-commit stash works somewhat differently from the later git stash apply or git stash pop of a two-commit stash, as well, though this answer does not address the later git stash apply or git stash pop at all.

2The push verb in git stash push is simply the opposite of the pop verb in git stash pop; it has nothing to do with git push. Back in the old days, git stash did not have a push sub-command. Some people noted that git stash had some weird syntactic issues and this asymmetry—pop without push—so eventually git stash acquired a push sub-command with better syntax and the ability to stash just some subset of files.

Note that when using the subset feature, the I and W commits you mentioned here are still full snapshots; it's just that the W snapshot only adds the specified subset files, and then the subsequent git reset + checkout that git stash runs only resets the specified subset as well. There was a very nasty, data-destroying bug in this new feature for several Git revisions: git stash push <subset> would save the subset, then reset too many files.

torek
  • 448,244
  • 59
  • 642
  • 775
  • Thanks. "whether or not the file is listed in a .gitignore", do you mean a file listed in .gitignore is also an untracked file? –  Jan 23 '19 at 01:45
  • By "the subsequent git reset + checkout that git stash runs ", do you mean `git stash pop` is implemented by invoking git reset and git checkout? –  Jan 23 '19 at 01:56
  • By "when using the subset feature", do you mean that `git stash push` always stores only the difference of the working tree or index from HEAD, and `git stash pop` always apply only the difference? –  Jan 23 '19 at 01:58
  • (1) No: a file listed in `.gitignore` is just listed there if it's tracked. If it's *untracked*, the entry in `.gitignore` tells Git: *don't complain about it, and don't auto-add the file when I say "add all files".* It also gives Git the ability to *overwrite or remove* the file in some cases (it means "this file is not very valuable"). (2) No: the `git reset` + checkout is the last part of `git stash save` / `git stash push`: having committed the files, Git *wipes them out*, replacing them with the versions from `HEAD`. – torek Jan 23 '19 at 01:59
  • (3): No: `git stash push`, because it has better syntax, allows you to say: *only save, then reset, some specific set of files, rather than all tracked files.* – torek Jan 23 '19 at 01:59