0

I had a few modified files in my develop branch and few that were newly created but untracked by git. I wanted to move these changes to a feature branch, So I ran git stash. After checking with git status, I realized that git did not stash the untracked files. So I ran git stash -u.

Something weird happened here, because after I checked out my feature branch and was trying to see the two stashed changes, using git stash show stash@{0} and git stash show stash@{1}, it did not show me anything for the 0th stash index and the 1st stash index showed the untracked files. There was no 2nd index.

git stash list did not show anything at all. I went ahead and did git stash apply and that only brought back the untracked file changes, not the modified files.

After I applied the untracked files changes, I ran git stash show stash@{0} and git stash show stash@{1} again. This time, the 0th index again was empty, and the modified files changes moved to the 1st index. Applying the stash@{1}, brought back all my changes, but I fail to understand the behavior of the stash. Has anybody seen this before?

SQB
  • 3,926
  • 2
  • 28
  • 49
Setafire
  • 719
  • 2
  • 9
  • 21

1 Answers1

1

For clarity:

$ git stash; git stash -u

This is two separate git stash commands. Each one does a git stash save since there is no more-specific verb at the end.

The first git stash saved away both your current index and your current work-tree (by making two separate commits, neither of which is on a branch). After that, your index matched the HEAD commit and your work-tree was clean, except of course for untracked files.

Normally, if you then tried to repeat git stash save, it would just tell you that there was nothing to save, and would do nothing, but for your second save you added the -u flag: save (and then remove) untracked files. So this made a second stash, using your current index—which the first save made equal to the HEAD commit—and your current work tree—which, again, is equal to the HEAD commit—and using a third commit as part of the stash-bag: the u commit. See the drawing in this answer, but note that you have two separate stash bags hanging off the same (HEAD) commit, in your particular case.

When you save a stash with some active stash(es), the stash script uses git's "reflogs" to "push" the current stash into the "stash stack", making the new stash the top-most one, stash@{0}. So your stash-bag with the u commit in it is, at this point, stash@{0}, while your stash with the modified-index-and/or-work-tree (and no u commit containing untracked files) is in stash@{1}.

You then did:

git checkout feature

or similar to change HEAD to point to a different branch. Let's draw the situation now, by assuming that the stash was done on a branch named X:

... - o - o - o   <-- HEAD=feature
  \
    o - *         <-- X
        |\
        |  \
        |\   \
        i-w    \      <-- stash@{1}
                |\
                i-w   <-- stash@{0}
                 /
                u

Again, both stash-bags hang off commit *; the one in stash@{1} has the untracked files, but its i and w commits are identical to commit * itself.

You said:

using git stash show stash@{0} and git stash show stash@{1}, it did not show me anything for the 0th stash index and the 1st stash index showed the untracked files ...

This seems unlikely. However, if you wrote git show stash@{0} instead of git stash show stash@{0}, and git show stash@{1} instead of git stash show stash@{1}, this is what you would have seen, because git show shows merge commits as "combined diffs", and you were basically asking git show to look at the w commit in each stash-bag. Each w commit is a merge commit, it's just not the usual kind of merge, so git show produces largely-useless results.

I went ahead and did git stash apply and that only brought back the untracked file changes, not the modified files.

That is what would be expected: git stash apply (which applies stash@{1}) tells git stash to compare the work-tree and index commits (w and i) to their parent (commit *), and in the case of a three-parent stash-bag, to extract the files in the third parent (u). There are no changes between w and * but this brings back u.

Applying the stash@{1}, brought back all my changes ...

Again, this is to be expected: git stash apply tells git stash to compare the work-tree (for stash@{1}) and index commits to their parent (*). Here, there are changes, and it extracts those and applies those to the new HEAD commit. There is no third parent in this other stash-bag, so apply is finished at this point.

If you like the result, go ahead and git stash drop the two stash-bags.

Community
  • 1
  • 1
torek
  • 448,244
  • 59
  • 642
  • 775
  • I appreciate your effort to answer. But I did see the exact behavior I have explained, with the 0th index being empty and that is what stumped me. I did not use `git show stash@{0}`, I used `git stash show stash@{0}` and the `git stash list` also did not show me any stashes. – Setafire Oct 08 '14 at 14:42
  • I'd like to see actual cut-and-paste output, although by now probably you don't have the stashes any more. In particular `git stash show` simply runs `git diff` between the base commit (the one marked `*` in this case) and the work-tree commit, so you should never see untracked files and showing stash 0 should be empty, while showing stash 1 should produce any work-tree changes (this would also be empty *if* you had `git add`-ed all your work, as all the changes would be in the index instead). – torek Oct 08 '14 at 14:52
  • BTW if you *do* still have them, the output from `git stash list` should not be empty either, and whatever the output is (although "empty" is not much to work with :-) ), it could be useful in trying to figure out how this is happening. – torek Oct 08 '14 at 14:59
  • No, I don't have them anymore. It wasn't even on my machine, it was happening on a colleague's machine. I was the one driving though. However, when I tried to reproduce on my machine, I couldn't get any of that behavior. I could see the stash list out both the stashes, and the 0th index wasn't empty in my case, either. – Setafire Oct 10 '14 at 18:30