Reputation: 749
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?
Upvotes: 0
Views: 504
Reputation: 487785
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}
andgit 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.
Upvotes: 1